簡體   English   中英

C ++ 17:始終調用用戶定義的構造函數,並且operator +重載不適用於子類

[英]C++17: user-defined constructor is always called and operator+ overloading not working for child class

總的來說,我正在實現一個類以模塊化無符號數字(無符號類型的包裝器)。 我希望我的班級符合以下條件:

  • 進行模數可完成的所有操作。
  • 帶下划線的value_type必須為無符號類型,但是模板參數可以轉換為無符號類型(例如,如果模板參數類型為int,則帶下划線的value_type將為無符號)。
  • 我想要一個可以執行額外操作(例如,除法)的派生類。
  • 它必須承認這些操作與可轉換為value_type的類型兼容(例如, Test<N> + unsignedTest<N> + UnsignedWrapper等)。
  • 必須允許隱式轉換為帶下划線的value_type。
  • 必須允許從Test<N>Test<K>顯式轉換(與TestChild相同),N和K是不同的數字。

我將問題簡化為以下代碼:

test.hpp:

#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
}

如果我取消對最后兩個和的注釋,則在Ubuntu 16.04 LTS中使用GCC 7.2.0使用g++ -std=c++17 -O2 -Wall -Wextra -pedantic test.cpp -o test編譯時,會出現此錯誤:

test.cpp: In function ‘int main()’:
test.cpp:92:10: error: ambiguous overload for ‘operator+’ (operand types are ‘Test<10000>’ and ‘Test<19847>’)
    u = u + w;   //The compiler complains about ambiguity (don't know how to fix it without casting w)
        ~~^~~
test.cpp:92:10: note: candidate: operator+(Test<10000>::value_type {aka unsigned int}, Test<19847>::value_type {aka unsigned int}) <built-in>
test.cpp:69:18: note: candidate: const Test<UInt> operator+(const Test<UInt>&, const U&) [with auto UInt = 10000; U = Test<19847>]
 const Test<UInt> operator+ (const Test<UInt>& lhs, const U& rhs){
                  ^~~~~~~~
test.cpp:74:18: note: candidate: const Test<UInt> operator+(const U&, const Test<UInt>&) [with auto UInt = 19847; U = Test<10000>]
 const Test<UInt> operator+ (const U& lhs, const Test<UInt>& rhs){
                  ^~~~~~~~
test.cpp: In instantiation of ‘const TestChild<UInt> operator+(const TestChild<UInt>&, const TestChild<UInt>&) [with auto UInt = 89209]’:
test.cpp:93:12:   required from here
test.cpp:65:35: error: could not convert ‘(* & lhs).TestChild<89209>::<anonymous>.Test<89209>::operator+=<TestChild<89209> >((* & rhs))’ from ‘Test<89209>’ to ‘const TestChild<89209>’
    return TestChild<UInt>(lhs) += rhs;
                                   ^~~

如您所見,我如何初始化變量並不重要。 始終調用用戶定義的構造函數。 我最初以為是對帶下划線的value_type的轉換進行了調用,但是后來我意識到情況並非如此,因為我將轉換顯式表示為value_type,但仍然不進行任何轉換。 我認為編譯器必須做一些奇怪的事情來避免復制構造函數或復制分配,但是我真的不知道。

我理解u = u + w;的歧義u = u + w; ,可以通過強制w進行固定; 但我想找到一種無需強制轉換的方法(我的意思是,也許可以推斷出u是一種類型,所以總和必須返回該類型)。

第二個總和是我不太了解編譯器錯誤的含義的總和。 我一直在尋找解決方案一個星期左右,但我不知道該怎么辦。 似乎它正確地獲得了函數簽名(調用了operator +的正確重載),但是隨后抱怨一些奇怪的類型轉換。

也許我對課程的設計很不滿意。 如果是這樣,請告訴我。

編輯:請注意, u = w + u也應該起作用,但這會導致另一個歧義,因此我決定強制進行強制轉換以便執行該操作。

這個:

u = u + w;

是模棱兩可的,因為您有兩個等於完全匹配的operator+()重載:

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);

這兩個都不比另一個更專業,因此編譯器無法知道要調用哪個(旁注,不要返回const prvalue)。 這里最簡單的解決方案是只添加第三個重載,這三個重載比這兩個都更專門:

template<auto UInt, auto UInt2>
Test<UInt> operator+(const Test<UInt>& lhs, const Test<UInt2>& rhs);

一個不太簡單的解決方案是約束一個或另一個- 不是 Test

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);

但這似乎過於復雜,可能不必要。


這個:

x = y + z;

會調用:

template<auto UInt>
const TestChild<UInt> operator+ (const TestChild<UInt>& lhs, const TestChild<UInt>& rhs);

它反過來在左側副本上調用operator+= ,實際上是Test<U>::operator+= ,它返回Test<U>

但是,您要調用的operator+返回一個TestChild<U> ,它是派生類-並且Test<U>不能隱式轉換為TestChild<U> 簡單的解決方法是僅執行加法,但將返回值分開:

template<auto UInt>
TestChild<UInt> operator+ (TestChild<UInt> lhs, const TestChild<UInt>& rhs) {
    lhs += rhs;
    return lhs;
}

現在一切都可以編譯了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM