Reputation: 115
I were using variadic templates in a program and it's come up an unexpected error. I isolated the error and I has shocked it:
#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;
}
If you delete the line "#include iostream", the source is compiled alright. However, if you put this line, the compiler throw an error:
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&&’
I'm using this g++ version: g++ (Ubuntu/Linaro 4.7.2-11precise2) 4.7.2
and I'm compiling with this flags: g++ -Wall -pedantic -std=c++11 prov.cpp -o prov
I don't understand why compiler throws this error. Is it a possible bug?
Upvotes: 0
Views: 280
Reputation: 56863
The problem is that <cctype>
defines a single function isspace
, but adding <iostream>
adds another overload for isspace
that is pulled in from <locale>
. The one from <cctype>
is
int isspace( int ch );
The one from <locale>
is
template< class charT >
bool isspace( charT ch, const locale& loc );
With both included, the std::isspace
becomes ambiguous and hence your code fails. This becomes visible only when you route it through your real ctor (instead of constructor
) because then the compiler can't decide what to forward. OTOH, the method constructor
takes a parameter which already tells the compiler how to choose from both overloads.
Upvotes: 2
Reputation: 131779
This is not a compiler bug or even a problem with variadic templates, std::isspace
is simply overloaded. When calling .constructor
directly, the first argument int (*f)(int)
gives the compiler enough information to select the correct overload, while a generic argument does not. This is easily demonstrated with an example:
// 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
}
You can fix this in two ways:
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
Upvotes: 3