2016-03-22 10 views
10

LWG 2424は、アトミック、ミューテックスおよび条件変数の望ましくない状態について、C++でtrivially copyableと述べています。修正はalready lined upですが、std::mutexstd::condition variableなどです。非自明なデストラクタを持つように見えます。たとえば:なぜmutexと条件変数は簡単にコピー可能ですか?

30.4.1.2.1クラスミューテックス[thread.mutex.class]

namespace std { 
    class mutex { 
    public: 
    constexpr mutex() noexcept; 
    ~mutex(); // user-provided => non-trivial 

    … 
    } 
} 

が、これは自明コピー可能としてそれらを失格ではないでしょうか?

+2

これはちょうどハワードが、おそらくそれを考えずに、議論の中で言ったものですか? – SergeyA

+0

'std :: mutex'は簡単にコピー可能ですか?これらのコンストラクタをコピーする標準とコピーの代入はともに 'delete'とマークされます。 – NathanOliver

+4

@ NathanOliverそれはLWG 1734の要点です(あなたが正しいとはいえ、 'mutex'は自明にコピーできません。) – Columbo

答えて

6

私の間違いだったか、私は誤って引用されていました。私は正直言ってそれを思い出しません。 is_trivialis_trivially_copyableを使用しないでください

はしかし、私は、件名に、この非常に強く開催されたアドバイスを持っています! EVER !!!

代わりにこれらのいずれかを使用します。

is_trivially_destructible<T> 
is_trivially_default_constructible<T> 
is_trivially_copy_constructible<T> 
is_trivially_copy_assignable<T> 
is_trivially_move_constructible<T> 
is_trivially_move_assignable<T> 

は理論的根拠:

tldr:このexcellent question and correct answer.

(私を含め)誰がis_trivialis_trivially_copyableの定義を思い出すことができます参照してください。 。もしあなたがそれを見上げて、それを分析するのに10分を費やしたら、それはあなたが直感的に思っていることをするかもしれないし、しないかもしれません。それを正しく分析できれば、CWGはほとんどまたはまったくその定義を変更してコードを無効にすることができます。

is_trivialis_trivially_copyableを使用しています。

しかしこれら:

is_trivially_destructible<T> 
is_trivially_default_constructible<T> 
is_trivially_copy_constructible<T> 
is_trivially_copy_assignable<T> 
is_trivially_move_constructible<T> 
is_trivially_move_assignable<T> 

彼らがやるような音、そしてこれまで彼らの定義が変更されている可能性はありません、正確に何をすべきか。特別なメンバーのそれぞれと個別に対処しなければならないことはあまりにも冗長に思えるかもしれません。しかし、それはあなたのコードの安定性/信頼性において恩恵を受けるでしょう。そしてもしあなたがそうしなければならないならば、これらの個々の形質をカスタム形質にまとめる。

更新

例えば、打ち鳴らす& gccが、このプログラムのコンパイル:

#include <type_traits> 

template <class T> 
void 
test() 
{ 
    using namespace std; 
    static_assert(!is_trivial<T>{}, ""); 
    static_assert(is_trivially_copyable<T>{}, ""); 
    static_assert(is_trivially_destructible<T>{}, ""); 
    static_assert(is_destructible<T>{}, ""); 
    static_assert(!is_trivially_default_constructible<T>{}, ""); 
    static_assert(!is_trivially_copy_constructible<T>{}, ""); 
    static_assert(is_trivially_copy_assignable<T>{}, ""); 
    static_assert(!is_trivially_move_constructible<T>{}, ""); 
    static_assert(is_trivially_move_assignable<T>{}, ""); 
} 

struct X 
{ 
    X(const X&) = delete; 
}; 

int 
main() 
{ 
    test<X>(); 
} 

X自明コピー可能ですが、はないは自明構築可能コピーすることを。私の知る限りでは、これは適合する行動です。

現在、VS-2015はXであると言います。もほとんどコピー可能ではありません。私はこれが現在の仕様によれば間違っていると信じていますが、私の常識が教えてくれるものと確かに一致します。

私はmemcpyに初期化されていないメモリに必要であれば、私はそのような操作は大丈夫だろうと、私を保証するためにis_trivially_copyableis_trivially_copy_constructibleを信頼します。 memcpyからに初期化されたのメモリを使用したい場合は、is_trivially_copy_assignableをチェックします。

+0

馬の口から!アドバイスをいただきありがとうございます。私はそれらの形質がC++であることを認識しませんでした17。あなたが 'std :: mutex'と会社の場合に、標準がユーザー提供のデストラクタを強制することを明確にしていますか?私は他の答えはこれが当てはまるとは思わないので尋ねます。 –

+1

はい、これは標準のファジー領域です。ミューテックスの仕様は、些細なことを念頭に置いて設計されていませんでした。そのような特性は変わるかもしれない。しかし、要約すると、私は、「トリビュアコピー可能」の定義が変わり、変更の対象となるため、ミューテックスが重要であるかどうかに関して仕様が何を言っているとは思わない。もっと重要な質問は次のとおりです。 mutexは簡単にコピーを割り当てることができますか?答えが移植可能ではない場合でも、後者の質問は移植可能であり、明確に答えられ、移植可能なコードになります。 –

+0

'is_trivially_copy_constructible'はどのようなものが役に立ちますか?それ自体はバイト単位のコピーを保証するものではありません。実際、 'is_trivially_copyable'の代わりに' is_trivially_copy_constructible'に助言するのは危険かもしれません。なぜなら、コピーコンストラクタが些細なのであれば 'memcpy'がうまくいると(擬似的に)推論できるからです。 – Columbo

3

これは言語弁護士の視点から見ると重要です。

mutexと条件変数などを実装することは、基本的に不可能です。ある時点では、デストラクタを記述する必要があり、そのデストラクタは非常に単純な作業を行う必要があります。

しかし、それは問題ではありません。どうして?標準では、そのような型は簡単にコピーできないと明示的に述べていないためです。従って、規格の観点からは、理論的にはが可能であり、そのようなオブジェクトは簡単にコピー可能である。

N4460の機能の実装はすべて可能ですが、そのようなタイプは決してコピー可能ではないことを非常に明確にすることです。

+0

実際にここで正しいアイディアを取ったかどうかはわかりません。私の見解では、削除された代入演算子/コピーctorsを** non ** - trivially copyableとしてタイプを判断することが正しい方法です。はい、それはいくつかの下位互換性を壊しますが、**正しいこと**です。 'operator ='で何かをコピーできない場合、なぜ 'memcpy()'でコピーするのが合法なのですか? – SergeyA

+0

しかし、標準では、これらの型に対してユーザー提供のデストラクタを指定していませんか? –

+1

@JosephThomson:デストラクタがあることを指定します。型は 'Destructibe'でなければなりません。しかし、標準では、デストラクタが自明ではないことを要求するものはありません。再度、言語弁護士のように考える。 「規則はない」ので、可能です。 –

4

すべての実装がmutexの重要なデストラクタを提供するわけではありません。 libstdC++を参照してください(と__GTHREAD_MUTEX_INITが定義されていることを前提としてい):

// Common base class for std::mutex and std::timed_mutex 
    class __mutex_base 
    { 
    // […] 
#ifdef __GTHREAD_MUTEX_INIT 
    __native_type _M_mutex = __GTHREAD_MUTEX_INIT; 

    constexpr __mutex_base() noexcept = default; 
#else 
    // […] 

    ~__mutex_base() noexcept { __gthread_mutex_destroy(&_M_mutex); } 
#endif 
    // […] 
    }; 

    /// The standard mutex type. 
    class mutex : private __mutex_base 
    { 
    // […] 
    mutex() noexcept = default; 
    ~mutex() = default; 

    mutex(const mutex&) = delete; 
    mutex& operator=(const mutex&) = delete; 
    }; 

mutexのこの実装は、(via Coliruを検証できる)標準準拠と自明コピー可能でもあります。同様に、実装では、condition_variableを徹底的に破壊できないようにすることはできません([thread.condition.condvar]/6を参照してください)。

最後に、condition_variableのことを賢明で微妙に解釈する必要はありません(また、それを達成する方法)。

+0

私はもう1つの答えで同じ質問をしましたが、標準で '〜mutex();'が存在すると、デストラクタをデフォルトにしてはならないことを指定していないか、クラスインタフェースがのように見える? –

+0

私の可能性のある誤解の根拠を示す質問を編集しました。 –

+0

@JosephThomsonいいえ、それは暗黙のうちではありません。 [\ [functions.within.classes \]/1](http://eel.is/c++draft/functions.within.classes#1)を参照してください。 – Columbo

関連する問題