与绑定一起使用时 is_base_of 的错误行为
将可变参数模板参数与简单模板参数一起使用时,当从绑定函子实例化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 这样的可变参数类型列表的第一种类型?为什么只有与绑定表达式一起使用时才会出现问题?
请注意,如果我将基本模板修改为只有一个模板参数,则绑定表达式将起作用:
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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您没有获得预期的输出,因为在
std::bind()
之后调用时Checker
函数对象中的First
数据类型类型为int&
,而不是int
。因此,
std::is_base_of , B>
不会实例化为std::true_type
来调用Checker::检查
。问题在于 std::bind 正在创建一个对象,该对象在内部存储您传递给它的函数的参数。因此,有一个命名的左值作为 std::bind 返回的对象的非静态数据成员,它保存您作为参数传递的值以绑定到您的函数。当该非静态数据成员在调用函子的operator()时传递给右值引用时,它会作为左值引用传递,因为它不是不再是一个临时对象。如果您执行以下操作,您也会遇到类似的问题:
命名值
x
是一个左值,并将被传递给中的
方法作为左值引用,而不是临时的,因为first
参数operator()first
是右值引用。因此,您的类型最终将再次成为int&
而不是int
,并且您将打印值0
。要解决此问题,您可以执行以下操作:
这将剥离对象的引用类型并为您提供所需的结果。
You're not getting the expected output because the data-type of
First
in yourChecker
function object when called afterstd::bind()
is of typeint&
, notint
.Therefore
std::is_base_of<Base<B,int&>, B>
does not instantiate to astd::true_type
for the call toChecker::check
.The problem is that
std::bind
is creating an object that internally stores the arguments for the function you are passing to it. Therefore there is a named l-value as a non-static data-member of the object returned bystd::bind
that is holding the value you passed as an argument to be bound to your function. When that non-static data-member is then passed to the r-value reference at the time you call theoperator()
of the functor, it's passed as an l-value reference, since it is no longer a temporary object. You would have a similar problem if you did something like:The named-value
x
is an l-value, and would be passed to thefirst
argument in youroperator()
method as an l-value reference, not as temporary, sincefirst
is a r-value reference. Therefore your type would end up again as anint&
and not anint
, and you'd print a value of0
.To fix this problem, you can do something like:
This will strip off the reference-type of the object and give you the results you want.
不幸的是 std::is_reference 没有给我关于更复杂问题的预期结果。
所以最后我选择提供引用和常量引用重载:
Unfortunately std::is_reference did not give me the expected result on a more complicated issue.
So finally I choosed providing the reference and const-reference overloads: