如果我将下面代码中的 CLASS 更改为 0 而不是 1,它将使用 gcc 或 clang 进行编译。但是,如果 CLASS 为 1,则两个编译器都将失败(在第二次使用 bar 时)。
#define CLASS 1
#include <utility>
void f(int *a);
template <typename Arg>
#if CLASS
struct bar {
#else
void
#endif
bar(Arg && arg) { f(std::forward<Arg>(arg)); }
#if CLASS
};
#endif
void foo()
{
int dummy;
int *p = &dummy;
#if !CLASS
#define a
#define b
#endif
bar a(p + 0);
bar b(p);
}
作为两个独立的片段,没有条件编译:
失败:
#include <utility>
void f(int *a);
template <typename Arg>
struct bar {
bar(Arg && arg) { f(std::forward<Arg>(arg)); }
};
void foo()
{
int dummy;
int *p = &dummy;
bar a(p + 0);
bar b(p);
}
成功:
#include <utility>
void f(int *a);
template <typename Arg>
void bar(Arg && arg) { f(std::forward<Arg>(arg)); }
void foo()
{
int dummy;
int *p = &dummy;
bar(p + 0);
bar(p);
}
最佳答案
template <typename Arg>
struct bar {
bar(Arg && arg) { f(std::forward<Arg>(arg)); }
};
和
template <typename Arg>
void bar(Arg && arg) { f(std::forward<Arg>(arg)); }
不是一回事。在函数 bar
中,您有一个转发引用。这允许您接受左值 (p
) 或右值 (p + 0
)。
使用 bar
结构,您不再拥有转发引用。它不再是转发引用的原因是因为它不再是函数模板参数。 T&&
只是在T
未知时的转发引用。由于您的 T
(Args
) 是已知的(当然不是真的,但它正在被推导,因此可以知道)这意味着它不再是转发引用。
这意味着您有一个对类模板类型的右值引用。这对
bar a(p + 0);
因为结果是一个临时的 int*
(int*&&
) 但是有
bar b(p);
你有一个 int*
(int*&
) 所以你不能将右值引用绑定(bind)到它。
如果你想让类的行为像函数一样,你需要类似的东西
struct bar {
template <typename Arg>
bar(Arg && arg) { f(std::forward<Arg>(arg)); }
};
否则,如果您只需要接受左值和右值,您可以添加重载来处理它
template <typename Arg>
struct bar {
bar(Arg & arg) { f(arg); }
bar(Arg && arg) { f(std::move(arg)); }
};
关于c++ - 在 C++17 中,为什么类模板与函数模板的指针类型推导明显不一致?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51483598/