xgbuils
xgbuils

Reputation: 115

Rare bug with variadic templates?

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

Answers (2)

Daniel Frey
Daniel Frey

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

Xeo
Xeo

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

Related Questions