我正在尝试创建一个函数,它以我的结构模板的对象作为参数。
template <unsigned dim>
struct vec{
float d[dim];
template<typename ...T>
vec(T&&... args) : d{args...}{}
float operator[] (unsigned n) const { return d[n]; }
// ...
};
这段代码本身工作得很好,但是当我想创建一个带有"vec"的函数时,它就开始麻烦了。对象作为形参。
void asdf(vec<3> a){ ... }
当我创建一个struct的实例作为参数输入时,它工作得很好:
asdf(vec<3>{5.f, 10.f, 3.f}); // Works fine
但是当我尝试这样做时,我的编译器不会买它:
vec<3> test{5.f, 10.f, 3.f};
asdf(test); // error: cannot convert 'vec<3>' to 'float' in initialization
我的IDE说问题在构造函数中。任何帮助将非常感激!
当传递vec
时,需要将构造函数模板从重载集合中排除。否则,它是精确匹配的,并且优先于接受const vec&
的复制构造函数(然后需要添加要调用的constness)。
。
template<typename T1, typename ...T, typename std::enable_if_t<!std::is_same_v<std::decay_t<T1>, vec>>* = nullptr>
vec(T1&& t, T&&... args) : d{std::forward<T1>(t), std::forward<T>(args)...}{}
正如@NathanOliver所建议的,如果使用构造函数模板作为默认构造函数,可能需要定义默认构造函数。
重载解析很棘手。完美匹配将在需要转换的匹配之前选择。
这个模板构造函数可以完美匹配任何东西:
template<typename ...T> vec(T&&... args);
复制构造函数是const vec类型的完美匹配,在这种情况下是比模板更好的匹配(因为所有条件相等,非模板函数被定义为更好的匹配)
vec(vec const& other); // copy constructor
在代码中,复制构造函数是由编译器隐式声明的。
现在,当你这样做的时候:
vec<3> test{5.f, 10.f, 3.f};
asdf(test); // error
问题是您正在创建一个非const"test"对象,因此重载解析会找到与模板化构造函数完美匹配的对象,但必须执行"const转换"。因此,它选择模板函数并失败。
然而,如果你将test
声明为一个const对象,它将按照你的期望编译和工作:
vec<3> const test{5.f, 10.f, 3.f}; // **** Notice, const now
asdf(test); // ok
你真正想做的是防止模板构造函数支配复制构造函数,即使对于const转换也是如此,这可以通过多种方式实现。
- 为构造函数添加约束以防止它匹配
vec
对象 - 用const和非const版本重载复制构造函数
在c++20中,第一种方法很简单。只需添加requires
子句,以确保您的参数都不是vec
类型:
template<typename ...T>
vec(T&&... args) requires (not (std::is_same_v<T, vec> && ...))
: d{args...}{}
std::enable_if
是在旧的编译器或语言级别上的另一种方法。
另一种方法是有"两个"。复制构造函数:
vec(vec const&) = default;
vec(vec & other) = default;
这包括const和非constvec
参数的两种情况。作为非模板,当传递一个vec时,其中一个类型会比模板匹配得更好,而其他类型仍然会选择模板。
如果你混合不同大小的向量,它仍然会失败,但是你可能不希望这样。但是如果你这样做了,你可以为它添加另一个模板:
// Only used when Size does not match "our size"
template<auto Size>
vec(vec<Size> const & other) {
}
你的问题是你的
template<typename ...T>
vec(T&&... args) : d{args...}{}
是贪婪的,将用于代替内置的复制构造函数,因为T&&
将解析为更好的匹配。要解决这个问题,只需要添加一个比模板更匹配的复制构造函数,可以通过添加
vec(vec&) = default; // for lvalues
vec(const vec&) = default; // for const lvalues
vec(vec&&) = default; // for rvalues
到你的类,你可以看到在这个实例中工作。