2

将可变参数模板参数与简单的模板参数一起使用时,我在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;
}
4

2 回答 2

2

您没有得到预期的输出,因为在调用后函数对象First中的数据类型是 type ,而不是。 Checkerstd::bind()int&int

因此std::is_base_of<Base<B,int&>, B>不会实例化到 astd::true_type来调用Checker::check

问题是std::bind创建一个对象,该对象在内部存储您传递给它的函数的参数。因此,有一个命名的左值作为返回的对象的非静态数据成员,std::bind它保存您作为参数传递的值,以绑定到您的函数。当那个非静态数据成员在你调用operator()仿函数的时候被传递给右值引用时,它作为左值引用被传递,因为它不再是一个临时对象。如果您执行以下操作,您将遇到类似的问题:

int x = 1;
Checker<B> ch2;
std::cout<<ch2(x, 3.14)<<std::endl;

命名值x是一个左值,并且将作为左值引用而不是临时传递给方法中的first参数,因为它是右值引用。因此,您的类型最终将再次成为 an而不是,并且您将打印.operator()firstint&int0

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

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)...);
    }
}

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

于 2011-08-19T16:25:23.190 回答
0

不幸的是 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)...);
}
于 2011-08-21T00:15:59.570 回答