c++ - 在 C++17 中,为什么类模板与函数模板的指针类型推导明显不一致?

标签 c++ c++17 rvalue-reference lvalue perfect-forwarding

如果我将下面代码中的 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);
}

https://godbolt.org/g/3hpq2u

作为两个独立的片段,没有条件编译:

失败:

#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/

相关文章:

c++ - 在 C++ 的析构函数中允许做什么?

c++ - boost.parameter 和构造函数

c++ - 在参数构造中使用 std::move

c++ - 是否可以将 "Prototype pattern"调整为右值引用?

C++ 使用 C struct 指针参数调用 C 函数

c++ - 如何在 C++ Autotools 项目中禁用 C 编译器

c++ - 将参数传递给另一个可变参数函数

c++ - 虚拟数组<in, 6> arr(); 失败

c++ - 展平一组类型,其中非类型值是展平的一部分

c++ - 延长临时对象的生命周期而不复制它