C++ C++;17:始终调用用户定义的构造函数和运算符+;重载不适用于子类
总的来说,我正在实现一个类来模块化无符号数(无符号类型的包装器)。我希望我的班级满足以下条件:C++ C++;17:始终调用用户定义的构造函数和运算符+;重载不适用于子类,c++,inheritance,constructor,c++17,C++,Inheritance,Constructor,C++17,总的来说,我正在实现一个类来模块化无符号数(无符号类型的包装器)。我希望我的班级满足以下条件: 执行模块化编号可以执行的所有操作 带下划线的值类型必须为无符号类型,但模板参数可以转换为无符号类型(例如,如果模板参数类型为int,则带下划线的值类型将为无符号) 我想要一个可以执行额外操作(例如除法)的派生类 它必须允许操作与可转换为值类型的类型兼容(例如,Test+unsigned,Test+UnsignedWrapper,…) 必须允许隐式转换为带下划线的值\u类型 必须允许从Test到Tes
- 执行模块化编号可以执行的所有操作
- 带下划线的值类型必须为无符号类型,但模板参数可以转换为无符号类型(例如,如果模板参数类型为int,则带下划线的值类型将为无符号)
- 我想要一个可以执行额外操作(例如除法)的派生类
- 它必须允许操作与可转换为值类型的类型兼容(例如,
,Test+unsigned
,…)Test+UnsignedWrapper
- 必须允许隐式转换为带下划线的值\u类型
- 必须允许从
到Test
(与TestChild相同)的显式转换,即N和K个不同的数字Test
#include <type_traits>
#include <iostream>
// As you can see, C++17 is needed
template <auto UInt>
class Test{
public:
//I need the type to be unsigned
typedef std::make_unsigned_t<decltype(UInt)> value_type;
static constexpr value_type N = static_cast<value_type>(UInt);
Test (const value_type& k = 0) : n(k%N){
// Just to show that this constructor is always called
std::cout << "Test user-defined constructor " << k << std::endl;
}
Test& operator+= (const Test& k){
n = (n+k.n)%N;
return *this;
}
template<typename U>
Test& operator+= (const U& other){
n = (n+static_cast<value_type>(other))%N;
return *this;
}
template<auto UInt2>
explicit operator Test<UInt2>() const{
return Test<UInt2>(n);
}
operator value_type() const{
// Just to show that this is called only once
std::cout << "Casting to value_type" << std::endl;
return n;
}
protected:
value_type n;
};
template<auto UInt>
class TestChild : public Test<UInt>{
public:
typedef typename Test<UInt>::value_type value_type;
static constexpr value_type N = Test<UInt>::N;
TestChild (const value_type& k = 0) : Test<UInt>(k){}
template<auto UInt2>
explicit operator TestChild<UInt2>() const{
return TestChild<UInt2>(this->n);
}
};
// I prefer to define binary operators outside the class and leave the logic inside the class
template<auto UInt>
const Test<UInt> operator+ (const Test<UInt>& lhs, const Test<UInt>& rhs){
return Test<UInt>(lhs) += rhs;
}
template<auto UInt>
const TestChild<UInt> operator+ (const TestChild<UInt>& lhs, const TestChild<UInt>& rhs){
return TestChild<UInt>(lhs) += rhs;
}
template<auto UInt, typename U>
const Test<UInt> operator+ (const Test<UInt>& lhs, const U& rhs){
return Test<UInt>(lhs) += static_cast<typename Test<UInt>::value_type>(rhs);
}
template<auto UInt, typename U>
const Test<UInt> operator+ (const U& lhs, const Test<UInt>& rhs){
return Test<UInt>(rhs) += static_cast<typename Test<UInt>::value_type>(lhs);
}
/****************************************************************************/
int main(){
// It doesn't matter how I initialize the varible,
// always calls the user-defined constructor
TestChild<89209> x(347), y(100), z(1000);
TestChild<89133> t = static_cast<decltype(t)>(x);
Test<10000> u = static_cast<decltype(u)>(y), v(z);
Test<19847> w(u);
TestChild<1297> r(u); //Here it seems that it casts u to its member value_type
u = u + v;
//u = u + w; //The compiler complains about ambiguity (don't know how to fix it without casting w)
//x = y + z; //No idea what's happening here
}
如您所见,如何初始化变量并不重要;始终调用用户定义构造函数。我最初认为调用了带下划线的value_类型的强制转换,但后来我意识到情况并非如此,因为我将强制转换明确化为value_类型,但它仍然不执行任何强制转换。我认为编译器一定在做一些奇怪的事情来避免复制构造函数或复制赋值,但我真的不知道
我理解u=u+w中的歧义代码>,可通过铸造w固定;但我想找到一种不用强制转换的方法(我的意思是,也许可以推断u是一种类型,所以求和必须返回该类型)
第二个和是我不太明白编译器错误的意思。我已经找了一个星期左右的解决方案,但我不知道该怎么办。它似乎得到了正确的函数签名(调用了运算符+的正确重载),但随后抱怨了一些奇怪的类型转换
也许我在设计这个班级。如果是这样,请告诉我
编辑:注意,u=w+u
也应该起作用,但这会导致另一个歧义,因此我决定强制强制强制转换以执行操作。这:
u = u + w;
不明确,因为您有两个完全匹配的运算符+()
重载:
template<auto UInt, typename U>
const Test<UInt> operator+(const Test<UInt>& lhs, const U& rhs);
template<auto UInt, typename U>
const Test<UInt> operator+(const U& lhs, const Test<UInt>& rhs);
一个不太简单的解决方案是约束其中一个,而不是一个测试:
template <typename T> struct is_test : std::false_type { };
template <auto V> struct is_test<Test<V>> : std::true_type { };
template<auto UInt, typename U,
std::enable_if_t<!is_test<U>{}, int> = 0>
Test<UInt> operator+(const U& lhs, const Test<UInt>& rhs);
将援引:
template<auto UInt>
const TestChild<UInt> operator+ (const TestChild<UInt>& lhs, const TestChild<UInt>& rhs);
现在所有的东西都编译好了。这是:
u = u + w;
不明确,因为您有两个完全匹配的运算符+()
重载:
template<auto UInt, typename U>
const Test<UInt> operator+(const Test<UInt>& lhs, const U& rhs);
template<auto UInt, typename U>
const Test<UInt> operator+(const U& lhs, const Test<UInt>& rhs);
一个不太简单的解决方案是约束其中一个,而不是一个测试:
template <typename T> struct is_test : std::false_type { };
template <auto V> struct is_test<Test<V>> : std::true_type { };
template<auto UInt, typename U,
std::enable_if_t<!is_test<U>{}, int> = 0>
Test<UInt> operator+(const U& lhs, const Test<UInt>& rhs);
将援引:
template<auto UInt>
const TestChild<UInt> operator+ (const TestChild<UInt>& lhs, const TestChild<UInt>& rhs);
现在一切都可以编译了。关于你的注释value\u type
必须是无符号的,如果这是一个真正的要求,我会使用std::enable\u if
和std::is\u unsigned
来编译。而不是强制使用无符号类型。问题很可能是因为您没有模板运算符+(const-Test&lhs,const-Test&rhs)
函数。@CoryKramer可能我没有很好地解释我的意思。我希望带下划线的类型是无符号的,但是模板参数可以转换为无符号类型(因此int被转换为unsigned…@Someprogrammerdude我认为模板操作符+(const Test&lhs,const U&rhs)
重载可以完成这项工作(其中U=Test
为UInt2任意数字)我认为如果这些类只是隐式地相互转换,就有可能使用这个重载。它们不是,因为您将转换运算符标记为显式的。关于注释value\u type
必须是无符号的,如果这是一个真正的要求,我会使用std::enable\u if
和std::is\u unsigned
来编译。而不是强制使用无符号类型。问题很可能是因为您没有模板运算符+(const-Test&lhs,const-Test&rhs)
函数。@CoryKramer可能我没有很好地解释我的意思。我希望带下划线的类型是无符号的,但是模板参数可以转换为无符号类型(因此int被转换为unsigned…@Someprogrammerdude我认为模板操作符+(const Test&lhs,const U&rhs)
重载可以完成这项工作(其中U=Test
为UInt2任意数字)我认为如果这些类只是隐式地相互转换,就有可能使用这个重载。它们不是,因为您将转换运算符标记为explicit
代码>我得到了同样的模糊性(一旦我定义了新的重载),所以我决定强制强制转换以执行操作。另外,我仍然不明白构造函数会发生什么,以及为什么总是调用用户定义的。@FranciscoGallegoSalido您需要实际读取错误消息吗<代码>u=w+u代码>在操作符=
上失败,而不是在操作符+
上失败。我知道这一点,但这可能会导致错误的结果。假设w+u
为Test
类型(因此求和分别来自Test),u为Test`类型。然后我可以得到一个不同于我想要的结果,所以我认为最明智的做法是强制施法代码>我得到了同样的模糊性(一旦我定义了新的重载),所以我决定强制强制转换以执行操作。另外,我仍然不明白构造函数会发生什么,以及为什么总是调用用户定义的。@FranciscoGallegoSalido您需要实际读取错误消息吗<鳕鱼