2017-02-03 53 views
5

是什麼,在C++標準而言,預期的(如果有的話)下面的程序的輸出:對於具有拋出拷貝構造函數和noexcept按值拷貝賦值的類,is_nothrow_copy_assignable的值是多少?

#include <iostream> 
#include <iomanip> 
#include <type_traits> 

class A { 
public: 
    A() = default; 
    ~A() = default; 
    A(A const& other) {} 
    A(A&& other) noexcept {} 
    A& operator=(A other) noexcept { return *this; } 
}; 

int main() { 
    std::cout << std::boolalpha 
     << std::is_nothrow_copy_assignable<A>::value << "\n" 
     << std::is_nothrow_move_assignable<A>::value << "\n"; 
} 

換句話說,不將型性狀值的分析看的聲明只有賦值運算符,這是noexcept,並且它從而得到

true 
true 

抑或是考慮調用上下文(abA實例)

a = b;   // may throw, implicitly calls copy c'tor 
a = std::move(b); // noexcept, implicitly calls move c'tor 

和它產生

false 
true 

實踐嘗試

運行與Visual Studio 2015的代碼,更新3給出

true 
true 

而GCC 6.1提供了

false 
true 

誰對?

背景

,當我們有一個投擲的拷貝構造函數的資源管理類(因爲資源分配可能會失敗),這樣的情況發生時,noexcept移動構造函數,一個投擲拷貝賦值和noexcept移動分配。

假設都複製和移動分配可以在交換IDOM方面能夠高效地實現:

A& operator=(A const& other) { 
    A(other).swap(*this); // calls the copy c'tor, may throw 
    return *this; 
} 

A& operator=(A&& other) noexcept { 
    A(std::move(other)).swap(*this); // calls noexcept move c'tor 
    return *this; 
} 

然後,我們可能會考慮冷凝雙雙進入單按值拷貝賦值

A& operator=(A other) noexcept { 
    other.swap(*this); 
    return *this; 
} 

但是,如果std::is_nothrow_copy_assignable<A>std::is_nothrow_move_assignable<A>提供了正確的值(分別爲false和true),我們只能安全地執行此操作。否則,依賴於這些類型特徵的代碼將表現得很差,我們的單個按值分配而不是是兩個單獨賦值運算符的正確替代。

+0

對於'vector',你需要'is_nothrow_move_assignable'是正確的,但不一定是'is_nothrow_copy_assignable'。也沒有必要使用'boolalpha'兩次。 –

+0

感謝您的評論。我已經編輯了相應的問題。當然,即使'std :: vector'不需要它,''nothrow_copy_assignable'仍然應該正確運行。 – jbab

+0

其實我很困惑。 'vector'要求'is_nothrow_move_constructible'是正確的,以在重新分配期間獲得完全的效率。我不記得它是否真的需要'is_nothrow_move_assignable'。但是,是的,無論「vector」是否關心它,性狀都應該正確。 –

回答

7

is_nothrow_copy_assignable定義在[meta.unary.prop]:

對於可引用類型T,相同的結果is_nothrow_assignable_v<T&, const T&>,否則false

好吧,A是可引用的(意思是A&是有效的)。所以我們進入is_nothrow_assignable

is_assignable_v<T, U>true和分配已知不會引發任何異常(5.3.7)。

is_assignable_v<A, A const&>絕對是true,所以我們滿足第一部分。知道不要拋出任何異常意味着什麼?根據[expr.unary.noexcept]:

noexcept操作員確定它的操作數,這是一個未計算的操作數(第5)的評價是否,可以拋出異常(15.1)。 [012]如果表達式(15.4)的潛在異常集爲空,則noexcept運算符的結果爲true,否則爲false。

而在[except.spec]:

的異常規範noexceptnoexcept(constant-expression),其中恆定表達產量true,表示異常規範是空集。的異常規範noexcept(constant-expression),其中常數表達式產生false,或在除此以外的函數聲明爲析構函數(12.4)或解除分配函數(3.7.4.2)不存在異常規範的表示 異常規範,即所有類型的集合。

和:

集合的表達式e的潛在的異常的是空的,如果e是一個核心常量表達式(5.20)。否則, 它是e的直接子表達式的潛在異常集合的並集,包括在函數調用中使用的默認 參數表達式,與由e形式定義的集合S組合,如下所示:[。 ..]
- 如果e隱式調用一個或多個函數(例如重載操作符,新表達式中的分配函數或者e是全表達式(1.9)的析構函數),則S是:
      - 在所有這些功能的異常規範類型集,並
      - 如果e是一個新的表達式[...]

現在,一個A的從A const&轉讓涉及兩個步驟:

  1. 調用的A
  2. 拷貝構造函數調用的A

異常規範的拷貝賦值運算符是這兩個函數的所有異常規範的聯合,這是所有類型的集合 - 因爲複製構造函數沒有例外規範。因此,is_nothrow_copy_assignable_v<A>應該是false。 gcc是正確的。

+0

問題實際上是什麼「賦值」意味着:單獨調用賦值運算符或整個賦值表達式。如果後者意味着(而且是),我認爲沒有任何疑問。 –

+0

@ T.C。我認爲,如果它特別指定了賦值運算符,那麼它會這麼說 - 而且這樣做反正也不會很實際。 – Barry