web-dev-qa-db-ja.com

intからunsignedintへの暗黙の変換を防ぐ方法は?

あなたがこれを持っているとしましょう:

struct Foo {
    Foo(unsigned int x) : x(x) {}
    unsigned int x;
};

int main() {
    Foo f = Foo(-1);     // how to get a compiler error here?
    std::cout << f.x << std::endl;
}

暗黙の変換を防ぐことは可能ですか?

私が考えることができる唯一の方法は、intを受け取り、intが負の場合に何らかのランタイムエラーを生成するコンストラクターを明示的に提供することですが、このためのコンパイラエラー。

重複があることはほぼ確実ですが、私が見つけた最も近いものは この質問 であり、暗黙の変換が許可される理由を尋ねます。

私はC++ 11とC++ 11以前のソリューションの両方、できれば両方で機能するソリューションに興味があります。

30

均一な初期化により、絞り込みが防止されます。

これは(要求どおりに機能しない)例に従います。

_struct Foo {
    explicit Foo(unsigned int x) : x(x) {}
    unsigned int x;
};

int main() {
    Foo f = Foo{-1};
    std::cout << f.x << std::endl;
}
_

可能な限り、均一な初期化(Foo(-1)の代わりに_Foo{-1}_)を使用することに慣れてください。

[〜#〜]編集[〜#〜]

別の方法として、コメントでOPから要求されたように、C++ 98でも機能するソリューションは、コンストラクターがprivate(_long int_など)を取得することをintとして宣言することです。
実際にそれらを定義する必要はありません。
別の回答で示唆されているように、_= delete_も良い解決策になることに注意してください。ただし、これもC++ 11以降のものです。

編集2

C++ 11以降は有効ですが、もう1つソリューション、イベントを追加したいと思います。
このアイデアはVooの提案に基づいており(詳細については、Brianの応答のコメントを参照)、コンストラクターの引数にSFINAEを使用しています。
これは、最小限の実用的な例に従います。

_#include<type_traits>

struct S {
    template<class T, typename = typename std::enable_if<std::is_unsigned<T>::value>::type>
    S(T t) { }
};

int main() {
    S s1{42u};
    // S s2{42}; // this doesn't work
    // S s3{-1}; // this doesn't work
}
_
29
skypjack

不要なオーバーロードを削除することで、コンパイルエラーを強制できます。

Foo(int x) = delete;
26
Brian

everyこのようなコードの発生について警告が必要で、GCCを使用している場合は、-Wsign-conversionオプションを使用してください。

foo.cc: In function ‘int main()’:
foo.cc:8:19: warning: negative integer implicitly converted to unsigned type [-Wsign-conversion]
     Foo f = Foo(-1);     // how to get a compiler error here?
                   ^

エラーが必要な場合は、-Werror=sign-conversionを使用してください。

8
Nate Eldredge