web-dev-qa-db-ja.com

非構築関数の暗黙的な変換を回避するにはどうすればよいですか?

非構築関数の暗黙的なキャストを回避するにはどうすればよいですか?
私は整数をパラメーターとして取る関数を持っています、
しかし、その関数は文字、ブール、ロングも受け取ります。
暗黙的にそれらをキャストすることでこれを行うと思います。
これを回避して、関数が一致するタイプのパラメーターのみを受け入れ、それ以外の場合はコンパイルを拒否するようにするにはどうすればよいですか?
「explicit」というキーワードがありますが、非構築関数では機能しません。 :\
私は何をしますか?

次のプログラムはコンパイルしますが、実行しないようにします。

#include <cstdlib>

//the function signature requires an int
void function(int i);

int main(){

    int i{5};
    function(i); //<- this is acceptable

    char c{'a'};
    function(c); //<- I would NOT like this to compile

    return EXIT_SUCCESS;
}

void function(int i){return;}

*用語や仮定の誤用を指摘してください

38
Trevor Hickey

charは自動的にintに昇格されるため、直接アクセスすることはできません。

トリックに頼ることができます:charをパラメーターとして取る関数を作成し、それを実装しないでください。コンパイルされますが、リンカーエラーが発生します。

void function(int i) 
{
}
void function(char i);
//or, in C++11
void function(char i) = delete;

charパラメーターを指定して関数を呼び出すと、ビルドが中断されます。

http://ideone.com/2SRdM を参照してください

用語:非解釈機能?コンストラクタではない関数ですか?

21
Luchian Grigore

他のすべてのタイプに一致する関数テンプレートを定義します。

void function(int); // this will be selected for int only

template <class T>
void function(T) = delete; // C++11 

これは、直接一致する非テンプレート関数が常に最初に考慮されるためです。次に、直接一致する関数テンプレートが考慮されます-したがって、function<int>は決して使用されません。しかし、charなどの他の場合は、function<char>が使用されます。これにより、コンパイルエラーが発生します。

void function(int) {}

template <class T>
void function(T) = delete; // C++11 


int main() {
   function(1);
   function(char(1)); // line 12
} 

エラー:

prog.cpp: In function 'int main()':
prog.cpp:4:6: error: deleted function 'void function(T) [with T = char]'
prog.cpp:12:20: error: used here

これはC++ 03の方法です:

// because this ugly code will give you compilation error for all other types
class DeleteOverload
{
private:
    DeleteOverload(void*);
};


template <class T>
void function(T a, DeleteOverload = 0);

void function(int a)
{}
53
PiotrNycz

functionがint以外で呼び出された場合にコンパイル時にエラーを引き起こす一般的な解決策を次に示します

template <typename T>
struct is_int { static const bool value = false; };

template <>
struct is_int<int> { static const bool value = true; };


template <typename T>
void function(T i) {
  static_assert(is_int<T>::value, "argument is not int");
  return;
}

int main() {
  int i = 5;
  char c = 'a';

  function(i);
  //function(c);

  return 0;
}

これは、引数の任意の型を機能させることで機能しますが、型レベルの述語としてis_intを使用します。 is_intの一般的な実装はfalse値を持っていますが、int型の明示的な特殊化は値trueを持っているため、静的アサートは引数がint型を持っていることを保証します。そうでない場合は、コンパイルエラーが発生します。

7
Geoff Reedy

さて、私は以下のコードでこれに答えるつもりでしたが、それがVisual C++で動作しても、望ましいコンパイルエラーを生成するという意味で、MinGW g ++ 4.7.1はそれを受け入れ、右辺値参照コンストラクターを呼び出します!

それはコンパイラのバグだと思いますが、私は間違っている可能性があります。

とにかく、これがmayが標準に準拠したソリューションであることが判明したコードです(または、それが私の側の思想だと判明するかもしれません!):

#include <iostream>
#include <utility>      // std::is_same, std::enable_if
using namespace std;

template< class Type >
struct Boxed
{
    Type value;

    template< class Arg >
    Boxed(
        Arg const& v,
        typename enable_if< is_same< Type, Arg >::value, Arg >::type* = 0
        )
        : value( v )
    {
        wcout << "Generic!" << endl;
    }

    Boxed( Type&& v ): value( move( v ) )
    {
        wcout << "Rvalue!" << endl;
    }
};

void function( Boxed< int > v ) {}

int main()
{
    int i = 5;
    function( i );  //<- this is acceptable

    char c = 'a';
    function( c );  //<- I would NOT like this to compile
}

おそらく、構造体を使用して2番目の関数をプライベートにすることができます。

#include <cstdlib>

struct NoCast {
    static void function(int i);
  private:
    static void function(char c);
};

int main(){

    int i(5);
    NoCast::function(i); //<- this is acceptable

    char c('a');
    NoCast::function(c); //<- Error

    return EXIT_SUCCESS;
}

void NoCast::function(int i){return;}

これはコンパイルされません:

prog.cpp: In function ‘int main()’:
prog.cpp:7: error: ‘static void NoCast::function(char)’ is private
prog.cpp:16: error: within this context
1
alestanis

C++ 14(およびC++ 11)の場合は、右辺値参照をオーバーロードすることで、コピーコンストラクターを無効にすることもできます。

例:基本の_Binding<C>_クラスがあるとします。ここで、Cは基本のConstraintクラスまたは継承されたクラスのいずれかです。ベクトルに_Binding<C>_を値で格納し、バインディングへの参照を渡し、暗黙的なコピーが発生しないようにしたいとします。

右辺値参照の特定のケースでは、(PiotrNyczの例に従って)func(Binding<C>&& x)を削除することでこれを行うことができます。

スニペット:

_template<typename T>
void overload_info(const T& x) {
  cout << "overload: " << "const " << name_trait<T>::name() << "&" << endl;
}

template<typename T>
void overload_info(T&& x) {
  cout << "overload: " << name_trait<T>::name() << "&&" << endl;
}

template<typename T>
void disable_implicit_copy(T&& x) = delete;

template<typename T>
void disable_implicit_copy(const T& x) {
  cout << "[valid] ";
  overload_info<T>(x);
}

...

int main() {
  Constraint c;
  LinearConstraint lc(1);

  Binding<Constraint> bc(&c, {});
  Binding<LinearConstraint> blc(&lc, {});

  CALL(overload_info<Binding<Constraint>>(bc));
  CALL(overload_info<Binding<LinearConstraint>>(blc));

  CALL(overload_info<Binding<Constraint>>(blc));

  CALL(disable_implicit_copy<Binding<Constraint>>(bc));
  // // Causes desired error
  // CALL(disable_implicit_copy<Binding<Constraint>>(blc));
}
_

出力:

_>>> overload_info(bc)
overload: T&&

>>> overload_info<Binding<Constraint>>(bc)
overload: const Binding<Constraint>&

>>> overload_info<Binding<LinearConstraint>>(blc)
overload: const Binding<LinearConstraint>&

>>> overload_info<Binding<Constraint>>(blc)
implicit copy: Binding<LinearConstraint>  ->  Binding<Constraint>
overload: Binding<Constraint>&&

>>> disable_implicit_copy<Binding<Constraint>>(bc)
[valid] overload: const Binding<Constraint>&
_

エラー(bazelで_clang-3.9_を使用、問題の行がコメント解除された場合):

_cpp_quick/prevent_implicit_conversion.cc:116:8: error: call to deleted function 'disable_implicit_copy'
  CALL(disable_implicit_copy<Binding<Constraint>>(blc));
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
_

完全なソースコード: prevent_implicit_conversion.cc

1
Eric Cousineau

私は最初にPiotrNyczのアプローチ(C++ 03の場合、プロジェクトで使用することを強いられます)を試しました。次に、より一般的なアプローチを見つけてこれを思いつきましたForcedType<T>テンプレートクラス。

template <typename T>
struct ForcedType {
    ForcedType(T v): m_v(v) {}
    operator T&() { return m_v; }
    operator const T&() const { return m_v; }

private:
    template <typename T2>
    ForcedType(T2);

    T m_v;
};

template <typename T>
struct ForcedType<const T&> {
    ForcedType(const T& v): m_v(v) {}
    operator const T&() const { return m_v; }

private:
    template <typename T2>
    ForcedType(const T2&);

    const T& m_v;
};

template <typename T>
struct ForcedType<T&> {
    ForcedType(T& v): m_v(v) {}
    operator T&() { return m_v; }
    operator const T&() const { return m_v; }

private:
    template <typename T2>
    ForcedType(T2&);

    T& m_v;
};

私が間違っていなければ、これらの3つの専門分野はすべての一般的なユースケースをカバーするはずです。右辺値参照(C++ 11以降)の特殊化が実際に必要かどうか、または値による1で十分かどうかはわかりません。

3番目のパラメーターが暗黙的な変換を許可しない3つのパラメーターを持つ関数の場合は、次のように使用します。

function(ParamType1 param1, ParamType2 param2, ForcedType<ParamType3> param3);
0
Fabio A.