簡體   English   中英

繼承模板化的operator = in C ++ 14:與g ++和clang ++的不同行為

[英]Inheriting templated operator= in C++14: different behaviour with g++ and clang++

我有這個代碼與GCC 9.1一樣正常工作:

#include <type_traits>

template< typename T >
class A
{
protected:
    T value;

public:
    template< typename U,
              typename...,
              typename = std::enable_if_t< std::is_fundamental< U >::value > >
    A& operator=(U v)
    {
        value = v;
        return *this;
    }
};

template< typename T >
class B : public A<T>
{
public:
    using A<T>::operator=;

    template< typename U,
              typename...,
              typename = std::enable_if_t< ! std::is_fundamental< U >::value > >
    B& operator=(U v)
    {
        this->value = v;
        return *this;
    }
};

int main()
{
    B<int> obj;
    obj = 2;
}

(實際上,我們會在B::operator=做一些奇特的事情,甚至為enable_if使用不同的類型特征,但這是最簡單的可重現的例子。)

問題是thtat Clang 8.0.1給出了一個錯誤,不管怎樣,不考慮父類的operator= ,盡管孩子已經using A<T>::operator=;

test.cpp:39:9: error: no viable overloaded '='
    obj = 2;
    ~~~ ^ ~
test.cpp:4:7: note: candidate function (the implicit copy assignment operator) not viable:
      no known conversion from 'int' to 'const A<int>' for 1st argument
class A
      ^
test.cpp:4:7: note: candidate function (the implicit move assignment operator) not viable:
      no known conversion from 'int' to 'A<int>' for 1st argument
class A
      ^
test.cpp:20:7: note: candidate function (the implicit copy assignment operator) not
      viable: no known conversion from 'int' to 'const B<int>' for 1st argument
class B : public A<T>
      ^
test.cpp:20:7: note: candidate function (the implicit move assignment operator) not
      viable: no known conversion from 'int' to 'B<int>' for 1st argument
class B : public A<T>
      ^
test.cpp:28:8: note: candidate template ignored: requirement
      '!std::is_fundamental<int>::value' was not satisfied [with U = int, $1 = <>]
    B& operator=(U v)
       ^
1 error generated.

哪個編譯器符合標准? (我正在使用-std=c++14編譯。)我應該如何更改代碼以使其正確?

考慮這個簡化的代碼:

#include <iostream>

struct A
{
    template <int n = 1> void foo() { std::cout << n; }
};

struct B : public A
{
    using A::foo;
    template <int n = 2> void foo() { std::cout << n; }
};

int main()
{
    B obj;
    obj.foo();
}

這兩個編譯器都應該打印2。

如果派生類已經具有相同簽名的類,則它隱藏或覆蓋using聲明引入的類。 你的賦值運算符的簽名表面上是相同的。 考慮這個片段:

template <typename U, 
          typename = std::enable_if_t<std::is_fundamental<U>::value>>
void bar(U) {}
template <typename U, 
          typename = std::enable_if_t<!std::is_fundamental<U>::value>>
void bar(U) {}

這會導致兩個編譯器的bar重新定義錯誤。

但是,如果更改其中一個模板中的返回類型,則錯誤消失!

現在是時候密切關注標准了。

當using-declarator將基類的聲明帶入派生類時,派生類中的成員函數和成員函數模板覆蓋和/或隱藏具有相同名稱的成員函數和成員函數模板,參數類型列表(11.3。 5),cv-qualification和基類中的ref-qualifier(如果有的話)(而不是沖突)。 這些隱藏或重寫的聲明被排除在using-declarator引入的聲明集之外

現在,就模板而言,這聽起來很可疑。 如何在不比較模板參數列表的情況下甚至比較兩個參數類型列表? 前者取決於后者。 的確,上面的一段說:

如果命名空間作用域或塊作用域中的函數聲明與using聲明引入的函數具有相同的名稱和相同的參數類型列表(11.3.5),並且聲明不聲明相同的函數,則程序為病態的。 如果命名空間作用域中的函數模板聲明具有相同的名稱,參數類型列表,返回類型和模板參數列表作為由using聲明引入的函數模板,則該程序是錯誤的。

這更有意義。 如果模板參數列表相同,則兩個模板是相同的,以及其他所有模板...但等等,這包括返回類型! 如果兩個模板的名稱和簽名中的所有內容( 包括返回類型 (但不包括默認參數值))相同,則它們是相同的。 然后一個人可以與另一個發生沖突或隱藏。

那么如果我們在B中更改賦值運算符的返回類型並使其與A中的相同,會發生什么? GCC停止接受該代碼

所以我的結論是這樣的:

  1. 當涉及到隱藏使用聲明帶來的其他模板的模板時,標准尚不清楚。 如果它意味着從比較中排除模板參數,它應該這樣說,並澄清可能的含義。 例如,函數是否可以隱藏函數模板,反之亦然? 在任何情況下,有一個在之間的標准語言不明原因的不一致性using的命名空間范圍和using帶來的基類的名稱派生類。
  2. GCC似乎using在命名空間范圍內using的規則,並將其應用於基類/派生類的上下文中。
  3. 其他編譯器會做其他事情。 究竟究竟是什么並不太清楚; 可能比較參數類型列表而不考慮模板參數(或返回類型),正如標准的字母所說,但我不確定這是否有意義。

注意: 我覺得這個答案是錯誤的, nm的答案是正確的。 我會保留這個答案,因為我不確定,但請去檢查答案。


Per [namespace.udecl] / 15

using聲明將基類中的名稱帶入派生類范圍時,派生類中的成員函數和成員函數模板覆蓋和/或隱藏具有相同名稱的成員函數和成員函數模板,parameter-type-list([ dcl.fct]),cv-qualification和ref-qualifier (如果有的話)在基類中(而不是沖突)。

operator=在派生類聲明B具有完全相同的名稱,參數類型列表,CV-資格(無),和REF-限定符 (無)作為一個中聲明A 因此,在B聲明的那個隱藏了A那個,並且代碼是格式錯誤的,因為重載解析沒有找到適當的調用函數。 但是,此處未解決模板參數列表。

他們應該考慮一下嗎? 這是標准變得不清楚的地方。 AB被認為具有Clang的相同(模板)簽名,但不被GCC認可。 nm的答案指出真正的問題實際上在於返回類型。 (確定簽名時從不考慮默認模板參數。)

請注意,這是在名稱查找時決定的。 模板參數推斷尚未執行,也不是替換。 你不能說“哦,扣除/替換失敗,所以讓我們去添加更多成員到重載集”。 所以SFINAE在這里沒有什么不同。

暫無
暫無

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

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