2013-03-15 50 views
0

我在程序中使用了可變參數模板,並且出現了一個意外的錯誤。我孤立的錯誤,我震驚了它:與可變參數模板的罕見錯誤?

#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; 
} 

如果刪除行「#包括的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 ,我與編譯這標誌:g++ -Wall -pedantic -std=c++11 prov.cpp -o prov

我不明白爲什麼編譯器會拋出這個錯誤。這是一個可能的錯誤?

+2

你給它一個重載的函數,編譯器應該如何在你想要的std :: isspace的哪個重載之間選擇? – PlasmaHH 2013-03-15 14:11:44

+5

爲什麼你馬上認爲它是一個編譯器錯誤?這是非常有信心... – 2013-03-15 14:13:24

+0

我不假設它是一個編譯器錯誤。在標題中我使用了疑問句,並且在帖子中,我想知道這是否是一個可能的錯誤。 – xgbuils 2013-03-15 14:35:36

回答

2

的問題是,<cctype>定義一個函數isspace,但加入<iostream>增加了一個過載isspace該從<locale>被拉入。從<cctype>的一個是

int isspace(int ch); 

<locale>的一個是

template< class charT > 
bool isspace(charT ch, const locale& loc); 

使用這兩種在內,std::isspace變得模糊,因此你的代碼失敗。這隻有當你通過你的真正的ctor(而不是constructor)路由它時纔可見,因爲編譯器無法決定轉發什麼。 OTOH,方法constructor採用了一個參數,它已經告訴編譯器如何從兩個過載中進行選擇。

+0

非常感謝。現在我已經解決了這個問題。 – xgbuils 2013-03-15 14:52:32

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