C++ 使用模板将语义从一种类型移动到另一种类型
是否可能有一个自身带有移动构造函数的结构a和一些可以从其他类型(例如结构B)移动的构造函数,但有一个模板演绎,这样类型B就不会直接硬编码为另一个移动构造函数:C++ 使用模板将语义从一种类型移动到另一种类型,c++,templates,c++11,c++14,move-semantics,C++,Templates,C++11,C++14,Move Semantics,是否可能有一个自身带有移动构造函数的结构a和一些可以从其他类型(例如结构B)移动的构造函数,但有一个模板演绎,这样类型B就不会直接硬编码为另一个移动构造函数: struct A{ A()= default; A(A&&a){ /* A's move constructor */ } template<typename T> A(T&&t){ /* (not a move constr
struct A{
A()= default;
A(A&&a){ /* A's move constructor */ }
template<typename T>
A(T&&t){
/* (not a move constructor! by std., matches also lvalues)
move from t
(meta programming to check if we can move the type)
*/
}
}
struct B{};
如何实现这一点?只需在构造函数中取一个B&&值,就像在构造函数中取a一样。只需在构造函数中取一个B&&值,就像在构造函数中取a一样。如果希望构造函数模板接受任意右值,可以在以下情况下使用enable\u:
如果希望构造函数模板接受任意右值,可以在以下情况下使用enable_:
最干净的解决方案是为左值创建更好的构造函数,然后=删除它: 如果您不知道这两个选项中的哪一个是您想要的,那么SFINAE的使用可能只值得这么复杂——也就是说,您从其他地方继承了构造函数
还有最后一个角落案例-AT const&&和AA const&&在某些角落案例中可能会出现。我让它像一个左值向量,因为你不能从一个T常量开始移动&通常。除非T的内脏是可变的,否则我会认为这是个坏主意。 < P>最干净的解决方案是为LValk创建一个更好的构造函数,然后删除它: 如果您不知道这两个选项中的哪一个是您想要的,那么SFINAE的使用可能只值得这么复杂——也就是说,您从其他地方继承了构造函数
还有最后一个角落案例-AT const&&和AA const&&在某些角落案例中可能会出现。我让它像一个左值向量,因为你不能从一个T常量开始移动&通常。除非T的内脏是可变的,否则我会认为这是个坏主意。注意T&&T是一个转发引用,它允许T推断不同于AB和&B的引用类型。所以这个构造器实际上也会匹配左值。这正是我要如何区分它的问题,但是保持模板推导过程继续进行…?可能有一些sfinae技巧,但我想不出任何东西。这将是另一个问题的主题!你的问题变了。FFS@Gabriel我强烈建议回到原来的问题并单独发布新问题,你所做的只是让人恼火的注意T&&T是一个转发引用,它允许T推断出不同于AB&&b的引用类型。所以这个构造器实际上也会匹配左值。这正是我要如何区分它的问题,但是保持模板推导过程继续进行…?可能有一些sfinae技巧,但我想不出任何东西。这将是另一个问题的主题!你的问题变了。FFS.@Gabriel我强烈建议回到原来的问题并单独发布新问题,你所做的只是让人恼火,所以用这种方式,SFINAE帮助只接受右值:-那是我在寻找默认模板参数的FINAE works吗?typename std::enable_if::value>::type*=nullptr似乎是常用的模式。无论如何,在这两种情况下都会发生编译器错误。但是,使用typename std::enable_if::value>::type*=nullptr生成的错误消息应该更具启发性。@Lingxi是的,SFINAE对模板参数很有效。我更喜欢这个,因为对我来说,它比隐藏在函数参数中更明显;但是你可以用你更喜欢的。例如,Clang为这两者都生成了非常好的消息。如果您真的关心错误消息,您可以添加一个重载,它只接受左值引用,并在其中添加静态断言。与其使用否定检查,为什么不测试std::is_右值_reference::value?太棒了,所以在这种情况下,SFINAE只接受右值是有帮助的:-这是我在寻找默认模板参数的Finae works吗?typename std::enable_if::value>::type*=nullptr似乎是常用的模式。无论如何,在这两种情况下都会发生编译器错误。但是,使用typename std::enable_if::value>::type*=nullptr生成的错误消息应该更具启发性。@Lingxi是的,SFINAE对模板参数很有效。我更喜欢这个,因为对我来说,它比隐藏在函数参数中更明显;但是你可以用你更喜欢的。例如,Clang为这两者都生成了非常好的消息。如果你真的关心错误消息,你可以添加一个重载,只接受左值引用,并在其中添加静态断言。与其使用否定检查,为什么不测试std::is _右值_reference::value?这是另一个解决方案,如果我们想在a中硬编码该类型,是的+1这是另一个解决方案,如果我们想在A中硬编码该类型,是的+1仅仅删除t&构造函数还不够吗?它是否也适用于常数t&?@Yam t const&&将比t&&更好地匹配t&&呢。T const&&有点奇怪,但例如int const foo{return 3;}有一个
常量值返回类型。这是非常奇怪和罕见的,您可能会忽略考虑它,并且在一个大型的、活动的代码库中数年或更长的时间内看不到问题,当您看到它发生时,可能是其他一些代码被梨形化的症状。但更具体的例子是,一个元组作为rvalue:std::getstd::forwardtup传递将是一个int-const&&我认为。@Yakk由于某些原因,foo的类型不是int-const,而是int,因为int是一个基本类型,请参见[expr]/6。但是你的论点适用于类类型。@Yakk-Oops,没有看到第二个-,我以为你在删除t&和const-t&。对不起,@ Dyp C++是一种愚蠢的语言,如果没有,那就是:糟糕的是,在讨论左值、右值和常量时,我永远不应该使用int,除非我想讨论内置类型所包含的异常。就像我做了一个返回数组的函数一样糟糕仅仅删除t&构造函数还不够吗?它是否也适用于常数t&?@Yam t const&&将比t&&更好地匹配t&&呢。T const&&有点奇怪,但例如int const foo{return 3;}有一个const rvalue返回类型。这是非常奇怪和罕见的,您可能会忽略考虑它,并且在一个大型的、活动的代码库中数年或更长的时间内看不到问题,当您看到它发生时,可能是其他一些代码被梨形化的症状。但更具体的例子是,一个元组作为rvalue:std::getstd::forwardtup传递将是一个int-const&&我认为。@Yakk由于某些原因,foo的类型不是int-const,而是int,因为int是一个基本类型,请参见[expr]/6。但是你的论点适用于类类型。@Yakk-Oops,没有看到第二个-,我以为你在删除t&和const-t&。对不起,@ Dyp C++是一种愚蠢的语言,如果没有,那就是:糟糕的是,在讨论左值、右值和常量时,我永远不应该使用int,除非我想讨论内置类型所包含的异常。就像我做了一个返回数组的函数一样糟糕
B b;
A a(std::move(b)); // select the templated constructor (which moves)
A a(b); // selects the same copy constructor (which moves but we do not want!!
template <
class T,
class Sfinae = typename std::enable_if<!std::is_lvalue_reference<T>::value>::type
>
A(T &&t)
template <
class T,
class Sfinae = typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type
>
A(T &&t)
struct A{
A()= default;
A(A&&a){ /* A's move constructor */ }
template<class T>
A(T&&t){
}
template<class T>
A(T const&&)=delete;
template<class T>
A(T&t)=delete;
};
struct A{
A()= default;
A(A&&a){ /* A's move constructor */ }
template<class T>
A(T&&t){
// rvalues end up here
}
template<class T>
A(T const&&t):A(t) {} // forward to lvalue ctor
template<class T>
A(T&t){
// lvalues end up here
}
};