2012-12-13 36 views
15

我试图创建一个可变模板类,它为类型列表中的每个类提供了一个方法。示出了一个例子,低于该在类型串的每个类创建一个print方法:从GCC 4.6.3在注释行中的错误模糊函数调用C++基类

#include <iostream> 
#include <string> 

// Helper class providing a function call 
template <typename T> 
class PrintHelper 
{ 
public: 
    void print(const T& t) { std::cout << t << std::endl; } 
}; 

// Provides a print method for each type listed 
template <typename... Ts> 
class Printer : public PrintHelper<Ts>... 
{}; 

int main() 
{ 
    Printer<int, std::string> p; 
    p.print(std::string("Hello World")); // Ambiguous Call 
} 

的注释行的结果。解决歧义的正确方法是什么?或者我应该看看不同的设计?

+0

与gcc 4.7.2相同的问题(在[liveworkspace](http://liveworkspace.org/code/1a4YN8$0))。感谢复制/可移植的示例顺便说一句。 –

+1

铛3.2树干165721说:'错误:在不同类型的多个基类中发现成员'打印'' – user786653

+2

另请参见[这个问题](http://stackoverflow.com/questions/5368862/why-do-multiple-inherited -functions与 - 相同的名称,但是,不同的签名而不是)。这实质上是一个隐藏规则和解包可变参数模板参数非常有限的能力相结合的问题。 –

回答

8

我不喜欢回答我自己的问题,但我从这里的评论中创建了以下解决方案。它将所有print函数纳入范围,并允许在所有函数上进行C++重载解析。

#include <iostream> 
#include <string> 

// Helper class providing a function call 
template <typename T> 
class PrintHelper 
{ 
public: 
    void print(const T& t) { std::cout << t << std::endl; } 
}; 

// Provides a print method for each type listed 
template <typename... Ts> 
class Printer 
{}; 

template<typename T> 
class Printer<T> : public PrintHelper<T> 
{ 
public: 
    using PrintHelper<T>::print; 
}; 

template<typename T, typename... Ts> 
class Printer<T, Ts...>: public PrintHelper<T>, public Printer<Ts...> 
{ 
public: 
    using PrintHelper<T>::print; 
    using Printer<Ts...>::print; 
}; 

int main() 
{ 
    Printer<int, std::string> p; 
    p.print("Hello World"); // Not an ambiguous Call 
} 
+0

+1这太棒了,为什么我没有想到这一点......所以我想这就是你如何解开variadic模板的使用声明。 –

+0

回答你自己的问题是非常好的(甚至明确鼓励)。这对未来的读者是有利的。参看http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/。你提出的解决方案也应该是公认的解决方案。 –

9

要解决的不确定性,这是可以做到

template <typename... Ts> 
struct Printer : PrintHelper<Ts>... 
{ 
    template <typename U> 
    void print (const U& t) 
    { 
     PrintHelper<U>::print (t); 
    } 
}; 

(见an example

但这是不太一样强大人们希望。特别是,您无法打印可转换为类型列表中某种类型的对象。

使用某些模板元编程,可以将其分派到正确的打印机。要做到这一点,你必须从Ts...中选择一种可以转换为U的类型,并调用右边的PrintHelper,即。

PrintHelper<typename find_convertible<U, Ts...>::type>::print (t); 

其中find_convertible<U, Ts...>

定义
template <typename U, typename... Ts> 
struct find_convertible 
{}; 

template <typename U, typename V, typename... Ts> 
struct find_convertible<U, V, Ts...> : 
    std::conditional< 
     std::is_convertible<U, V>::value, 
     std::common_type<V>, // Aka identity 
     find_convertible<U, Ts...> 
    >::type 
{}; 

(见example

+0

代码的美丽和平! –

+0

谢谢你的帮助。第一个例子对我的用例来说已经足够了。继上面的一条评论之后,还有另一种可能的解决方案,通过改变继承来允许转换(http://liveworkspace.org/code/2PpiGS$1)。这将使用与您的方法相反的C++重载解决方案,它将采用类型列表中的第一个可转换类型。 –

+0

@PeterOgden:我真的很喜欢你的其他方法。您能否将其作为未来参考的答案发布?在线pastebins不会永远持续下去。 –

3

下面的代码可以解决的模糊问题:

#include <iostream> 
#include <string> 

// Helper class providing a function call 
template <typename T> 
class PrintHelper 
{ 
    protected: 
    void print_impl(const T& t) { std::cout << t << std::endl; } 
}; 

// Provides a print method for each type listed 
template <typename... Ts> 
class Printer : public PrintHelper<Ts>... 
{ 
    public: 
    template <typename U> 
    void print(const U& u) { 
     PrintHelper<U>::print_impl(u); 
    }; 
}; 

int main() 
{ 
    Printer<int, std::string> p; 
    p.print(std::string("Hello World")); // Ambiguous Call 
} 

这是不是很漂亮,因为类型U(在通话中推导出)恰好就是其中之一的要求e可变类型列表中的类型。你可能会喜欢一些东西来解决这个问题。有了一些模板魔法和Sfinae,你可以很容易地解决这个问题(但它肯定不是那么整洁)。

歧义问题与使用模板或可变模板无关,当然,它是成员查找规则的直接应用(标准的10.2.2节),即通常称为“会员隐藏规则”。如果你把这个简单的非模板版本,你会得到同样的模糊问题,但有一个非常简单的解决方案吧:

struct IntPrinter { 
    void print(const int& i) { std::cout << i << std::endl; }; 
}; 

struct StringPrinter { 
    void print(const std::string& s) { std::cout << s << std::endl; }; 
}; 

struct IntStringPrinter : IntPrinter, StringPrinter { 
    using IntPrinter::print;  // These using-statements will solve the problem 
    using StringPrinter::print; // by importing all 'print' functions to the same 
           // overload resolution level. 
}; 

所以,这里的问题确实是歧义产生的编译器之前甚至试图以应用正常的重载解析规则,因为它首先试图找出继承的哪个分支来查找成员函数,然后,它将仅在一个继承级别解析重载。使用可变参数模板时的问题是,似乎没有办法解压缩一组“使用”语句以将所有打印函数导入到相同的继承级别。如果有人知道解压这些使用陈述的方法,我全都听过。您可能需要回退到pre-variadic-template解决方案(如具有10个参数的常规模板,并专门针对所有10个版本)。