与bind一起使用时is_base_of的错误行为

时间:2011-08-19 14:58:27

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

使用可变参数模板参数和简单的模板参数我从绑定仿函数实例化时遇到了 is_base_of 的一些奇怪行为。

以下是代码:

template <class T, class Index>
class Base{};

template<typename T>
struct Checker {
    typedef int result_type;

    // Returns 1 if a given T type is descendant of Base<T,First>
    template<typename First, typename ...Args>
    result_type operator()(First&& first, Args&&... params)
    {
        return check(std::is_base_of<Base<T,First>, T>(),
                std::forward<First>(first),
                std::forward<Args>(params)...);
    }
    template<typename ...Args>
    result_type check(const std::true_type&, Args&&... params)
    {
        return 1;
    }
    template<typename ...Args>
    result_type check(const std::false_type&, Args&&... params)
    {
        return 0;
    }
};

struct A {};
struct B : Base<B,int> {};

int main()
{
    Checker<A> ch1;
    std::cout<<ch1(3.14)<<std::endl;
    Checker<B> ch2;
    std::cout<<ch2(1 ,3.14)<<std::endl; // output is 1
    std::cout<<std::bind(ch2, 1, 3.14)()<<std::endl; // output is 0 but it should be 1 !
    return 0;
}

程序输出为:

0
1
0

但我希望:

0
1
1

我是否以错误的方式使用可变参数模板?有没有其他(正确的)方法来获得第一种类型的可变参数类型列表,如Args?为什么只有当它与绑定表达式一起使用时才会出现问题?

注意,如果我修改Base模板只有一个模板参数,那么绑定表达式可以工作:

template <class T>
class Base{};

template<typename T>
struct Checker {
    typedef int result_type;

    // Returns 1 if a given T type is descendant of Base<T>
    template<typename ...Args>
    result_type operator()(Args&&... params)
    {
        return check(std::is_base_of<Base<T>, T>(),
                std::forward<Args>(params)...);
    }
    template<typename ...Args>
    result_type check(const std::true_type&, Args&&... params)
    {
        return 1;
    }
    template<typename ...Args>
    result_type check(const std::false_type&, Args&&... params)
    {
        return 0;
    }
};

struct A {};
struct B : Base<B> {};

int main()
{
    Checker<A> ch1;
    std::cout<<ch1(3.14)<<std::endl;
    Checker<B> ch2;
    std::cout<<ch2(3.14)<<std::endl; // output is 1
    std::cout<<std::bind(ch2, 3.14)()<<std::endl; // output is 1 this time!
    return 0;
}

2 个答案:

答案 0 :(得分:2)

您未获得预期的输出,因为在First之后调用时Checker函数对象中std::bind()的数据类型属于int&类型,而不是{ {1}}。

因此,对于int的调用,std::is_base_of<Base<B,int&>, B>无法实例化为std::true_type

问题是Checker::check正在创建一个对象,该对象在内部存储您传递给它的函数的参数。因此,有一个命名的l值作为std::bind返回的对象的非静态数据成员,它保存您作为参数传递的值以绑定到您的函数。当您在调用仿函数的std::bind时将该非静态数据成员传递给r值引用时,它将作为l值引用传递,因为它不再是临时对象。如果你做了类似的事情,你会遇到类似的问题:

operator()

命名值int x = 1; Checker<B> ch2; std::cout<<ch2(x, 3.14)<<std::endl; 是一个l值,并且会作为l值引用传递给x方法中的first参数,而不是临时值,因为operator()是r值参考。因此,您的类型将再次以first而不是int&结束,并且您打印的值为int

要解决此问题,您可以执行以下操作:

0

这将剥离对象的引用类型,并为您提供所需的结果。

答案 1 :(得分:0)

不幸的是,std :: is_reference没有给我一个更复杂问题的预期结果。 所以最后我选择提供引用和const-reference重载:

template<typename First, typename ...Args>
result_type operator()(First& first, Args&&... params)
{
    return check(std::is_base_of<Base<T,First>, T>(),
            first,
            std::forward<Args>(params)...);
}
template<typename First, typename ...Args>
result_type operator()(const First& first, Args&&... params)
{
    return check(std::is_base_of<Base<T,First>, T>(),
            first,
            std::forward<Args>(params)...);
}