C++:将基类型完美透明地包装到一个类中?

标签 c++ c++11 type-conversion ambiguous

我正在尝试开发一个完美包装基类型的类,以便人们可以像平常一样使用它们,但也可以扩展它们/添加新功能(c++11/c++14/etc 都很好) .到目前为止我还没有成功。这是我遇到的一个例子(这里我只实现了加号运算符,但我最终会实现它们):

template <class T>
class N
{
public:
  N() {};
  N(int _var) : var(_var) {};

  template<typename U = T,
           typename Arg,
           typename Return = 
           typename std::conditional<std::is_same<typename std::remove_cv<U>::type, long double>::value || std::is_same<typename std::remove_cv<Arg>::type, long double>::value, long double, 
            typename std::conditional<std::is_same<typename std::remove_cv<U>::type, double>::value || std::is_same<typename std::remove_cv<Arg>::type, double>::value, double, 
             typename std::conditional<std::is_same<typename std::remove_cv<U>::type, float>::value || std::is_same<typename std::remove_cv<Arg>::type, float>::value, float, 
              typename std::conditional<std::is_same<typename std::remove_cv<U>::type, unsigned long long>::value || std::is_unsigned<Arg>::value, unsigned long long, 
               typename std::conditional<std::is_same<typename std::remove_cv<U>::type, long long>::value || std::is_same<typename std::remove_cv<Arg>::type, long long>::value || std::is_same<typename std::remove_cv<U>::type, unsigned long long>::value || std::is_same<typename std::remove_cv<Arg>::type, unsigned long long>::value, long long, 
                typename std::conditional<std::is_same<typename std::remove_cv<U>::type, unsigned long>::value || std::is_unsigned<Arg>::value, unsigned long, 
                 typename std::conditional<std::is_same<typename std::remove_cv<U>::type, long>::value || std::is_same<typename std::remove_cv<Arg>::type, long>::value || std::is_same<typename std::remove_cv<U>::type, unsigned long>::value || std::is_same<typename std::remove_cv<Arg>::type, unsigned long>::value, long, 
                  typename std::conditional<std::is_same<typename std::remove_cv<U>::type, unsigned int>::value || std::is_unsigned<Arg>::value, unsigned int, 
                   typename std::conditional<std::is_same<typename std::remove_cv<U>::type, int>::value || std::is_same<typename std::remove_cv<Arg>::type, int>::value || std::is_same<typename std::remove_cv<U>::type, unsigned int>::value || std::is_same<typename std::remove_cv<Arg>::type, unsigned int>::value, int, 
                    typename std::conditional<std::is_same<typename std::remove_cv<U>::type, unsigned short>::value || std::is_unsigned<Arg>::value, unsigned short, 
                     typename std::conditional<std::is_same<typename std::remove_cv<U>::type, short>::value || std::is_same<typename std::remove_cv<Arg>::type, short>::value || std::is_same<typename std::remove_cv<U>::type, unsigned short>::value || std::is_same<typename std::remove_cv<Arg>::type, unsigned short>::value, short, 
                      typename std::conditional<std::is_same<typename std::remove_cv<U>::type, unsigned char>::value || std::is_unsigned<Arg>::value, unsigned char, 
                       typename std::conditional<std::is_same<typename std::remove_cv<U>::type, char>::value || std::is_same<typename std::remove_cv<Arg>::type, char>::value || std::is_same<typename std::remove_cv<U>::type, unsigned char>::value || std::is_same<typename std::remove_cv<Arg>::type, unsigned char>::value, char, 
                        typename std::conditional<std::is_same<typename std::remove_cv<U>::type, bool>::value, bool, void
                                                 >::type 
                                                >::type 
                                               >::type 
                                              >::type 
                                             >::type 
                                            >::type 
                                           >::type 
                                          >::type 
                                         >::type 
                                        >::type 
                                       >::type 
                                      >::type 
                                     >::type 
                                    >::type 
          >
  N<Return> operator+(const N<Arg>& i) const
    { return var+i; };

//#define CONVERSION(x)   template<typename U = T, typename = typename std::enable_if< std::is_same<U, x>::value >::type> operator x() const { return var; };
#define CONVERSION(x)   operator x() const { return var; };
  CONVERSION(bool);
  CONVERSION(char);
  CONVERSION(unsigned char);
  CONVERSION(short);
  CONVERSION(unsigned short);
  CONVERSION(int);
  CONVERSION(unsigned int);
  CONVERSION(long);
  CONVERSION(unsigned long);
  CONVERSION(long long);
  CONVERSION(unsigned long long);
  CONVERSION(float);
  CONVERSION(double);
  CONVERSION(long double);

  T var;
};

int main(int, char**)
{
  N<short> n1;
  N<int> n2;
  int d = n1;
  n1 + n2;
  return 0;
}

因此,基本上如您所见,我正在为它阐明条件系列中类型之间最小无损转换的基本逻辑。但是运行这个得到:

test.cpp: In instantiation of ‘N<Return> N<T>::operator+(const N<Arg>&) const [with U = short int; Arg = int; Return = int; T = short int]’:
test.cpp:2542:8:   required from here
test.cpp:2507:17: error: ambiguous overload for ‘operator+’ (operand types are ‘const short int’ and ‘const N<int>’)
     { return var+i; };
                 ^
test.cpp:2507:17: note: candidate: operator+(int, int) <built-in>
test.cpp:2507:17: note: candidate: operator+(int, unsigned int) <built-in>
test.cpp:2507:17: note: candidate: operator+(int, long int) <built-in>
test.cpp:2507:17: note: candidate: operator+(int, long unsigned int) <built-in>
test.cpp:2507:17: note: candidate: operator+(int, long long int) <built-in>
test.cpp:2507:17: note: candidate: operator+(int, long long unsigned int) <built-in>
test.cpp:2507:17: note: candidate: operator+(int, float) <built-in>
test.cpp:2507:17: note: candidate: operator+(int, double) <built-in>
test.cpp:2507:17: note: candidate: operator+(int, long double) <built-in>

...因为转换运算符都可以被调用,所以编译器认为所有这些对于我明确说明的转换来说都是同样有效的选项。现在,如果我们切换到注释掉的转换宏,以便为每种类型只启用一个转换运算符,我们将得到:

test.cpp: In function ‘int main(int, char**)’:
test.cpp:2541:11: error: cannot convert ‘N<short int>’ to ‘int’ in initialization
   int d = n1;
           ^

...因为编译器拒绝转换为 short 然后将其转换为 int,所以它需要显式的直接转换运算符。

我知道我显然可以通过在 main 函数中使用显式强制转换来做到这一点。但这不是重点,重点是透明包装,一个行为就像基类型(不需要显式转换)的类。

就目前而言,这在 C++ 中实际上是不可能的吗? :(

最佳答案

我想我明白了每个人!这不是最漂亮的代码,但它似乎可以工作。首先,我会注意到我将问题分解为两个变体:“StrongNumber”,它试图始终返回一个包装的数字类;和“WeakNumber”,它总是尝试返回一个基本类型。关键是 - 是什么让它变得丑陋 - 避免使用模板。模板只会导致编译器混淆。

template <class T>
class StrongNumber
{
public:
  StrongNumber() {};
  StrongNumber(const T& _var) : var(_var) {};
  StrongNumber(const StrongNumber<T>& i) : var(i.var) {};

  #define DECLARE_HELPER(x, y) \
  auto operator x (const StrongNumber<long double>& i) y { return StrongNumber<decltype(var x i.var)>(var x i.var); };  \
  auto operator x (const StrongNumber<double>& i) y { return StrongNumber<decltype(var x i.var)>(var x i.var); };   \
  auto operator x (const StrongNumber<float>& i) y { return StrongNumber<decltype(var x i.var)>(var x i.var); };    \
  auto operator x (const StrongNumber<unsigned long long>& i) y { return StrongNumber<decltype(var x i.var)>(var x i.var); };   \
  auto operator x (const StrongNumber<long long>& i) y { return StrongNumber<decltype(var x i.var)>(var x i.var); };    \
  auto operator x (const StrongNumber<unsigned long>& i) y { return StrongNumber<decltype(var x i.var)>(var x i.var); };\
  auto operator x (const StrongNumber<long>& i) y { return StrongNumber<decltype(var x i.var)>(var x i.var); };     \
  auto operator x (const StrongNumber<unsigned int>& i) y { return StrongNumber<decltype(var x i.var)>(var x i.var); }; \
  auto operator x (const StrongNumber<int>& i) y { return StrongNumber<decltype(var x i.var)>(var x i.var); };      \
  auto operator x (const StrongNumber<unsigned short>& i) y { return StrongNumber<decltype(var x i.var)>(var x i.var); };   \
  auto operator x (const StrongNumber<short>& i) y { return StrongNumber<decltype(var x i.var)>(var x i.var); };    \
  auto operator x (const StrongNumber<unsigned char>& i) y { return StrongNumber<decltype(var x i.var)>(var x i.var); };\
  auto operator x (const StrongNumber<char>& i) y { return StrongNumber<decltype(var x i.var)>(var x i.var); };     \
  auto operator x (const StrongNumber<bool>& i) y { return StrongNumber<decltype(var x i.var)>(var x i.var); };     \
  auto operator x (const long double i) y { return StrongNumber<decltype(var x i)>(var x i); };         \
  auto operator x (const double i) y { return StrongNumber<decltype(var x i)>(var x i); };              \
  auto operator x (const float i) y { return StrongNumber<decltype(var x i)>(var x i); };               \
  auto operator x (const unsigned long long i) y { return StrongNumber<decltype(var x i)>(var x i); };      \
  auto operator x (const long long i) y { return StrongNumber<decltype(var x i)>(var x i); };           \
  auto operator x (const unsigned long i) y { return StrongNumber<decltype(var x i)>(var x i); };           \
  auto operator x (const long i) y { return StrongNumber<decltype(var x i)>(var x i); };                \
  auto operator x (const unsigned int i) y { return StrongNumber<decltype(var x i)>(var x i); };            \
  auto operator x (const int i) y { return StrongNumber<decltype(var x i)>(var x i); };             \
  auto operator x (const unsigned short i) y { return StrongNumber<decltype(var x i)>(var x i); };          \
  auto operator x (const short i) y { return StrongNumber<decltype(var x i)>(var x i); };               \
  auto operator x (const unsigned char i) y { return StrongNumber<decltype(var x i)>(var x i); };           \
  auto operator x (const char i) y { return StrongNumber<decltype(var x i)>(var x i); };                \
  auto operator x (const bool i) y { return StrongNumber<decltype(var x i)>(var x i); };

  #define DECLARE(x)       DECLARE_HELPER(x, )
  #define DECLARE_CONST(x) DECLARE_HELPER(x, const)
  #define DECLARE_UNARY(x) auto operator x () { return StrongNumber<decltype(x var)>(x var); };
  #define DECLARE_UNARY_CONST(x) auto operator x () const { return StrongNumber<decltype(x var)>(x var); };

  operator T() const { return var; };

  DECLARE_CONST(%);
  DECLARE_CONST(+);
  DECLARE_CONST(-);
  DECLARE_CONST(*);
  DECLARE_CONST(/);
  DECLARE_CONST(&);
  DECLARE_CONST(|);
  DECLARE_CONST(^);
  DECLARE_CONST(<<);
  DECLARE_CONST(>>);
  DECLARE(=);
  DECLARE(+=);
  DECLARE(-=);
  DECLARE(*=);
  DECLARE(/=);
  DECLARE(&=);
  DECLARE(|=);
  DECLARE(^=);
  DECLARE(<<=);
  DECLARE(>>=);
  DECLARE_CONST(==);
  DECLARE_CONST(!=);
  DECLARE_CONST(>);
  DECLARE_CONST(<);
  DECLARE_CONST(>=);
  DECLARE_CONST(<=);
  DECLARE_UNARY(++);
  DECLARE_UNARY(--);
  DECLARE_UNARY_CONST(+);
  DECLARE_UNARY_CONST(-);
  DECLARE_UNARY_CONST(~);
  DECLARE_UNARY_CONST(!);
  explicit operator std::string() const { return std::to_string(var); };
  static int size() { return sizeof(T); };

  T var;
};

template <class T>
class WeakNumber
{
public:
  WeakNumber() {};
  WeakNumber(const T& _var) : var(_var) {};
  WeakNumber(const WeakNumber<T>& i) : var(i.var) {};

  #undef DECLARE_HELPER
  #define DECLARE_HELPER(x, y) \
  auto operator x (const WeakNumber<long double>& i) y { return var x i.var; }; \
  auto operator x (const WeakNumber<double>& i) y { return var x i.var; };      \
  auto operator x (const WeakNumber<float>& i) y { return var x i.var; };       \
  auto operator x (const WeakNumber<unsigned long long>& i) y { return var x i.var; };  \
  auto operator x (const WeakNumber<long long>& i) y { return var x i.var; };   \
  auto operator x (const WeakNumber<unsigned long>& i) y { return var x i.var; };   \
  auto operator x (const WeakNumber<long>& i) y { return var x i.var; };        \
  auto operator x (const WeakNumber<unsigned int>& i) y { return var x i.var; };    \
  auto operator x (const WeakNumber<int>& i) y { return var x i.var; };     \
  auto operator x (const WeakNumber<unsigned short>& i) y { return var x i.var; };  \
  auto operator x (const WeakNumber<short>& i) y { return var x i.var; };       \
  auto operator x (const WeakNumber<unsigned char>& i) y { return var x i.var; };   \
  auto operator x (const WeakNumber<char>& i) y { return var x i.var; };        \
  auto operator x (const WeakNumber<bool>& i) y { return var x i.var; };        \
  auto operator x (const long double i) y { return var x i; };          \
  auto operator x (const double i) y { return var x i; };           \
  auto operator x (const float i) y { return var x i; };            \
  auto operator x (const unsigned long long i) y { return var x i; };       \
  auto operator x (const long long i) y { return var x i; };            \
  auto operator x (const unsigned long i) y { return var x i; };        \
  auto operator x (const long i) y { return var x i; };             \
  auto operator x (const unsigned int i) y { return var x i; };         \
  auto operator x (const int i) y { return var x i; };              \
  auto operator x (const unsigned short i) y { return var x i; };       \
  auto operator x (const short i) y { return var x i; };            \
  auto operator x (const unsigned char i) y { return var x i; };        \
  auto operator x (const char i) y { return var x i; };             \
  auto operator x (const bool i) y { return var x i; };

  #undef DECLARE
  #undef DECLARE_CONST
  #undef DECLARE_UNARY
  #undef DECLARE_UNARY_CONST
  #define DECLARE(x)       DECLARE_HELPER(x, )
  #define DECLARE_CONST(x) DECLARE_HELPER(x, const)
  #define DECLARE_UNARY(x) auto operator x () { return x var; };
  #define DECLARE_UNARY_CONST(x) auto operator x () const { return x var; };

  operator T() const { return var; };

  DECLARE_CONST(%);
  DECLARE_CONST(+);
  DECLARE_CONST(-);
  DECLARE_CONST(*);
  DECLARE_CONST(/);
  DECLARE_CONST(&);
  DECLARE_CONST(|);
  DECLARE_CONST(^);
  DECLARE_CONST(<<);
  DECLARE_CONST(>>);
  DECLARE(=);
  DECLARE(+=);
  DECLARE(-=);
  DECLARE(*=);
  DECLARE(/=);
  DECLARE(&=);
  DECLARE(|=);
  DECLARE(^=);
  DECLARE(<<=);
  DECLARE(>>=);
  DECLARE_CONST(==);
  DECLARE_CONST(!=);
  DECLARE_CONST(>);
  DECLARE_CONST(<);
  DECLARE_CONST(>=);
  DECLARE_CONST(<=);
  DECLARE_UNARY(++);
  DECLARE_UNARY(--);
  DECLARE_UNARY_CONST(+);
  DECLARE_UNARY_CONST(-);
  DECLARE_UNARY_CONST(~);
  DECLARE_UNARY_CONST(!);
  // A couple more just because they're useful
  explicit operator std::string() const { return std::to_string(var); }; 
  static int size() { return sizeof(T); };

  T var;
};

所以,我在这里写了一个测试函数:

template<class T1, class T2, class T3>
T3 foo()
{
  StrongNumber<T1> n1;
  StrongNumber<T2> n2;
  T3 d = n1;
  n1 + n2;
  n1 + 1;
  n1 + 1.0;
  d = d + n1;
  d += 1 + n1;
  d += 1.0 + n1;
  return d;
}

...然后反复称它为:

std::cout << foo<short, long long, int>() << std::endl;

...等等。我让它尝试了基本类型的每一种组合(我写了一个快速的 bash 脚本来打印它们),但没有一个给出编译错误。

所以,我认为这已经解决了! :) 我发誓,我曾经编写过的 C++ 程序中有三分之一需要像这样的类,并且过去编写过一些不太令人满意的程序……所以这将得到很多用处!

关于C++:将基类型完美透明地包装到一个类中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34242451/

相关文章:

c++ - 为什么只有 std::atomic_flag 保证是无锁的?

c++ - 将 OpenCV 项目升级到最新版本

c++ - 项目的 Makefile

sqlite - 我应该如何处理 SQLalchemy 和 SQLite 中的小数

c - 如何在c中提取 double 值的小数部分而不进行四舍五入

C++0x : how to get variadic template parameters without reference?

c++ - 将二维的一部分复制到自身上

c - 在 C 中签名到无符号的转换 - 它总是安全的吗?

c++ - 将 const_iterator 分配给迭代器

c++ - 是否存在将 push_back 替换为 emplace_back 不正确的情况?