如何解决如何使用C ++中的通用引用参数为模板类编写构造函数
template <typename D,typename S>
struct MyClass {
private:
D d_;
S s_;
};
我想添加一个以D和S引用作为参数的构造函数,因此它可以 得到D和S推断:
template <typename D,typename S>
struct MyClass {
// can bind to lvalues
constexpr MyClass(const D &d,const S &s): d_(d),s_(s) {}
// can bind to rvalues
constexpr MyClass(D &&d,S &&s): d_(std::move(d)),s_(std::move(s)) {}
private:
D d_;
S s_;
};
效果很好。我可以从左值或右值创建对象:
auto c1 = MyClass(1,std::string("Hello")); // rvalues
auto idx = 2;
auto msg = std::string("World");
auto c2 = MyClass(idx,msg); // lvalues
但是我只想一个使用通用引用的构造函数,而不是两个 每个都处理左值和右值。大概是这样的:
template <typename D,typename S>
struct MyClass {
constexpr MyClass(D &&d,S &&s): d_(std::forward<D>(d)),s_(std::forward<S>(s)) {}
private:
D d_;
S s_;
};
它不适用于语句“ auto w2 = MyClass(i,j);”。编译器抱怨
error: no viable constructor or deduction guide for deduction of template arguments of 'MyClass'
auto c2 = MyClass(idx,msg); // lvalues
^
note: candidate function [with D = int,S = std::__1::basic_string<char>] not viable: no kNown conversion from 'int' to 'int &&' for
1st argument
constexpr MyClass(D &&d,s_(std::forward<S>(s)) {}
命令行:c++ -std=c++17 test.cpp
,用Apple clang version 12.0.0 (clang-1200.0.32.6)
编译
似乎构造函数只能接受右值。不是通用参考。所以我的问题是我可以实现通用参考样式构造函数吗? (我也想将MyClass保留为带有两个类型参数D和S的模板类。)
解决方法
在此摘要中, d 和 s 是 NOT 通用引用-它们是 rvalue引用(因为D 类实例已经在类中知道和推导了S和S。
template <typename D,typename S>
struct MyClass {
constexpr MyClass(D &&d,S &&s): d_(std::forward<D>(d)),s_(std::forward<S>(s)) {}
private:
D d_;
S s_;
};
根据Scott Meyers:,对于那些D&& and S&&
类型的变量(仅以这种形式!),需要涉及类型推演。成为通用引用,因此您需要额外的模板参数(以便通过 class-method (在这种情况下,= ctor)实例化推导得出类型):
template <typename D,typename S>
struct MyClass {
template <typename DD,typename SS>
constexpr MyClass(DD &&d,SS &&s): d_(std::forward<DD>(d)),s_(std::forward<SS>(s)) {}
private:
D d_;
S s_;
};
通过构造函数调用启用类临时类型推断可以通过类模板推断指南完成,例如:
template <typename DD,typename SS> MyClass(DD &&d,SS &&s) -> MyClass<DD,SS>
,
如果我们要按照您定义的方式初始化产品类型,则始终使用 汇总初始化 :
visible
并且如果在C ++ 20中或提供了推导指南,我们可以忽略模板参数:
MyClass<int,std::string>{ 1,"123"s }; // aggregate initialization won't generate deduction guide automatically until C++20.
有时,聚集初始化的要求无法满足。在这种情况下, 传递副本然后将其移动 对我们来说总是很便宜的:
MyClass{ 1,"123"s }; // after C++20,or by deduction guide.
但是如果移动也很昂贵,为了提高效率,我们应该使用 通用参考 :
template<typename... Types>
struct A{
A(Types... args) :members({ std::move(args)... }){};
std::tuple<Types...> members;
};
但是,我们丢失了类模板的模板参数中的类型信息,这意味着我们无法对诸如template<typename... Types>
struct A{
template<typename... Args>
A(Args&&... args) :members({ std::forward<Args>(args)... }){};
std::tuple<Types...> members;
};
之类的成员使用聚合初始化。
幸运的是,如果它不是可变参数模板,很容易解决此问题:
MyClass<std::tuple<int,int>,int>{{1,2},3}
对于可变参数模板,我们必须通过TMP方法来实现:
template<typename T,typename Y>
struct A{
template<typename P = T,typename Q = Y>
A(P&& p,Q&& q) :t(std::forward<P>(p)),y(std::forward<Q>(q)){};
T t;
Y y;
};
不幸的是,它破坏了零开销原则,因为存在巨大的空间成本( O(2 ^ n) )存储虚拟基类的指针。这只是理论上的。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。