C++11 与bind一起使用时,is_base_的错误行为

C++11 与bind一起使用时,is_base_的错误行为,c++11,bind,variadic-templates,typetraits,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,

使用可变模板参数和一个简单的模板参数,当它从绑定的函子实例化时,我经历了一些奇怪的行为,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
1
我是否以错误的方式使用可变模板?有没有其他(正确的)方法来获取变量类型列表的第一种类型,比如Args?为什么只有当它与bind表达式一起使用时才会出现问题

请注意,如果我将基本模板修改为只有一个模板参数,则绑定表达式可以工作:

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;
}
模板
类基{};
模板
结构检查器{
typedef int result_type;
//如果给定的T类型是Base的后代,则返回1
模板
结果类型运算符()(参数和参数)
{
返回检查(std::is_base_of(),
标准:正向(参数);
}
模板
结果类型检查(const std::true\u type&,Args&…params)
{
返回1;
}
模板
结果类型检查(常量std::false类型&,参数&…参数)
{
返回0;
}
};
结构A{};
结构B:Base{};
int main()
{
检查器ch1;

std::cout由于在
std::bind()
之后调用
Checker
函数对象时,
First
的数据类型为
int&
,而不是
int
,因此无法获得预期的输出

因此,
std::is_base_of
不会为调用
Checker::check
实例化为
std::true_类型

问题是,
std::bind
正在创建一个对象,该对象在内部存储要传递给它的函数的参数。因此,
std::bind
返回的对象的非静态数据成员中有一个命名的l值,该值保存了作为要绑定到函数的参数传递的值。当非静态数据成员在调用函子的
操作符()
时被传递到r值引用,它作为l值引用传递,因为它不再是临时对象。如果执行以下操作,则会出现类似问题:

int x = 1;
Checker<B> ch2;
std::cout<<ch2(x, 3.14)<<std::endl;
template<typename First, typename ...Args>
result_type operator()(First&& first, Args&&... params)
{
   if (std::is_reference<First>::value)
    {
        return check(std::is_base_of<Base<T, typename std::remove_reference<First>::type>, T>(),
            std::forward<First>(first),
            std::forward<Args>(params)...);
    }
    else
    {
        return check(std::is_base_of<Base<T,First>, T>(),
            std::forward<First>(first),
            std::forward<Args>(params)...);
    }
}

这将删除对象的引用类型,并提供所需的结果。

不幸的是,std::is_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)...);
}
模板
结果类型运算符()(第一个和第一个参数和…参数)
{
返回检查(std::is_base_of(),
第一,
标准:正向(参数);
}
模板
结果类型运算符()(常量First和First,参数&&…params)
{
返回检查(std::is_base_of(),
第一,
标准:正向(参数);
}

是的,确实如此。但是有没有办法以某种方式将其作为int传递,而不是int&?为什么以及如何将其变成int&而不是int?谢谢你,Jason,这帮了大忙。我想问一下,为什么要将其实现为函数而不是元函数?当所有检查都可以在编译时完成时,涉及一个值似乎完全没有必要。我很想知道,意外的结果是什么?顺便说一句,请记住常量左值引用不会绑定到非常量右值引用,因此如果没有该类型的特定重载,您将无法传递常量左值参数而不会导致编译器错误。只有常量右值引用才能绑定任何对象他们。
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)...);
}