【问题标题】:Rare bug with variadic templates?可变参数模板的罕见错误?
【发布时间】:2013-03-15 14:02:44
【问题描述】:

我在程序中使用可变参数模板,但出现了意外错误。我隔离了错误并震惊了它:

#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 我正在使用以下标志进行编译:g++ -Wall -pedantic -std=c++11 prov.cpp -o prov

我不明白为什么编译器会抛出这个错误。这可能是一个错误吗?

【问题讨论】:

  • 你给它一个重载的函数,编译器应该如何选择你想要的 std::isspace 的重载?
  • 为什么你马上就认为这是一个编译器错误?太有自信了……
  • 我不认为这是编译器错误。在标题中我使用了疑问句,在帖子中我想知道这是否是一个可能的错误。

标签: c++ templates c++11 variadic-templates


【解决方案1】:

这不是编译器错误,甚至不是可变参数模板的问题,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

【讨论】:

    【解决方案2】:

    问题在于&lt;cctype&gt; 定义了一个函数isspace,但添加&lt;iostream&gt; 会为isspace 添加另一个重载,该重载是从&lt;locale&gt; 拉入的。来自&lt;cctype&gt; 的是

    int isspace( int ch );
    

    来自&lt;locale&gt;的那个是

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

    同时包含两者,std::isspace 变得模棱两可,因此您的代码会失败。仅当您通过真实的ctor(而不是constructor)路由它时才可见,因为编译器无法决定转发什么。 OTOH,constructor 方法采用了一个参数,该参数已经告诉编译器如何从两个重载中进行选择。

    【讨论】:

    • 非常感谢。现在我已经理解了这个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-27
    • 2014-02-01
    • 2017-12-16
    • 2016-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多