我在一个程序中使用了可变参数模板,它出现了意想不到的错误。我把错误隔离了,我震惊了:
#include<cctype>
#include<iostream> // try to delete this line
class A
{
public:
void constructor()
{ }
template<typename... Args>
void constructor( int (*f)(int), Args... args )
{
// process( f )
constructor( args... );
}
template<typename... Args>
A( Args... args )
{
constructor( args... );
}
};
int main()
{
A a;
a.constructor( std::isspace ); // ok
A b( std::isspace ); // error
return 0;
}
如果删除“#include iostream”行,则源编译正常。但是,如果你输入这一行,编译器会抛出一个错误:
prov.cpp: In function ‘int main()’:
prov.cpp:32:22: error: no matching function for call to ‘A::A(<unresolved overloaded function type>)’
prov.cpp:32:22: note: candidates are:
prov.cpp:18:7: note: A::A(Args ...) [with Args = {}]
prov.cpp:18:7: note: candidate expects 0 arguments, 1 provided
prov.cpp:4:7: note: constexpr A::A(const A&)
prov.cpp:4:7: note: no known conversion for argument 1 from ‘<unresolved overloaded function type>’ to ‘const A&’
prov.cpp:4:7: note: constexpr A::A(A&&)
prov.cpp:4:7: note: no known conversion for argument 1 from ‘<unresolved overloaded function type>’ to ‘A&&’
我正在使用这个g ++版本:g ++(Ubuntu / Linaro 4.7.2-11precise2)4.7.2 我正在使用这些标志进行编译:{{1}}
我不明白为什么编译器会抛出此错误。这是一个可能的错误吗?
答案 0 :(得分:3)
这不是编译器错误,甚至是可变参数模板的问题,std::isspace
只是简单重载。直接调用.constructor
时,第一个参数int (*f)(int)
为编译器提供了足够的信息来选择正确的重载,而泛型参数则没有。通过一个例子可以很容易地证明这一点:
// our std::isspace
void foo(int){}
void foo(double){}
void test1(void (*f)(int)){}
template<class T>
void test2(T v){}
int main(){
test1(foo); // compiles, compiler sees you want the 'int' overload
test2(foo); // error, compiler has no clue which overload you want
// and which type to deduce 'T' as
}
您可以通过两种方式解决此问题:
int (*f)(int) = std::isspace; // assign to variable, giving the compiler the information
A b(f); // already has concrete type here
// or
A b(static_cast<int(*)(int)>(std::isspace)); // basically the same as above, in one step
答案 1 :(得分:2)
问题在于<cctype>
定义了单个函数isspace
,但添加<iostream>
会为从<locale>
引入的isspace
添加另一个重载。来自<cctype>
的那个是
int isspace( int ch );
<locale>
中的那个是
template< class charT >
bool isspace( charT ch, const locale& loc );
包含两者后,std::isspace
变得模棱两可,因此您的代码失败。只有当您通过真实的ctor(而不是constructor
)进行路由时,这才会变得可见,因为编译器无法决定要转发的内容。 OTOH,方法constructor
接受一个参数,该参数已告诉编译器如何从两个重载中进行选择。