web-dev-qa-db-ja.com

C ++での 'void'テンプレート引数の使用

次の最小限の例を見てください。

_using Type1 = std::function<void(void)>;

template <typename T>
using Type2 = std::function<void(T)>;

Type1 whyDoesThisWork;
Type2<void> andYetThisDoesNot;
_

2番目のタイプエイリアスの場合、「引数に 'void'タイプがない可能性があります」というエラーが表示されます。 (Xcode 4.5、Clang/c ++ 11/libc ++、OS X 10.7でテストしました。)

私はこれが不思議だと思います:_Type1_と_Type2<void>_が同じように動作することを期待していました。何が起きてる?そして、2番目のタイプのエイリアスを書き換える方法がありますので、私はcan_Type2<void>_を書き込み、代わりにstd::function<void(void)>を取得しますエラー?

編集おそらくこれを追加したい理由は、次のようなことを許可するためです。

_template <typename ... T>
using Continuation = std::function<void(T...)>;

auto someFunc = []() -> void {
  printf("I'm returning void!\n");
};

Continuation<decltype(someFunc())> c;
_

Continuation<decltype(someFunc())>は_Continuation<void>_になり、エラーが発生します。

20
Nick Hutchinson

短い答えは「テンプレートは文字列置換ではない」です。 void f(void)は、Cとの下位互換性を保つために、C++でのvoid f()のエイリアスである場合にのみ意味があります。

他の場所で述べたように、最初のステップは変分法を使用することです。

2番目のステップは、voidを返す関数を...にマップする方法を理解することです。まあ、std::function<void()>のようなものか、別の何かかもしれません。他の場合とは異なり、std::function<void()> foo; foo( []()->void {} );を呼び出すことができないため、他のことを言っているかもしれません。これは、真の継続ではありません。

このようなものは多分:

template<typename T>
struct Continuation
{
  typedef std::function<void(T)> type;
};

template<>
struct Continuation<void>
{
  typedef std::function<void()> type;
};

次に、次のように使用します。

auto someFunc = []()->void {};
Continuation<decltype(someFunc())>::type c;

これにより、必要なタイプが得られます。継続への適用を追加することもできます。

template<typename T>
struct Continuation
{
  typedef std::function<void(T)> type;

  template<typename func, typename... Args>
  static void Apply( type const& cont, func&& f, Args... args)
  {
    cont( f(args...) );
  }
};

template<>
struct Continuation<void>
{
  typedef std::function<void()> type;
  template<typename func, typename... Args>
  static void Apply( type const& cont, func&& f, Args... args)
  {
    f(args...);
    cont();
  }
};

これにより、入力型がvoid型または非void型の場合、関数の実行に継続を均一に適用できます。

しかし、「なぜあなたはこれをしたいのですか?」

実際の答えはありません。コメントで言ったことだけです。次のように、関数の型としてvoidを含めることはできません。

_int foo(int, char, void, bool, void, void);     // nonsense!
_

T(void)はCの互換性表記としてのみ許可されていると思います(これはdeclarationsprototypesを区別し、C++とは非常に異なります)。 「引数なし」と言うことができます)。

したがって、ソリューションは可変的でなければなりません:

_template <typename ...Args> using myType = std::function<void(Args...)>;
_

そうすれば引数なしを正しく持つことができます:

_myType<> f = []() { std::cout << "Boo\n"; }
_
12
Kerrek SB

いくつかの回答がすでにその理論的根拠を説明しています。これらの答えに追加するために、仕様には(C++ 11§8.3.5[dcl.func]/4)と記載されています。

非依存型voidの単一の名前のないパラメーターで構成されるパラメーターリストは、空のパラメーターリストと同等です。この特別な場合を除いて、パラメーターの型はcvvoidであってはなりません。

_Type2_の例では、void(T)T依存型-であり、テンプレートパラメータに依存します。

5
James McNellis

std::function<void(void)>のように、関数がvoidタイプのパラメーターを取るように宣言されている場合、これは実際には、パラメーターがゼロであることを示す間抜けな方法です。しかし、Type2を宣言した方法は、std::functionは何も返さない(void)が、1つのパラメーターをとるシグニチャーを使用します。 voidはパラメーターとして使用できる型ではなく、パラメーターがないことを宣言するための単なる方法です。したがって、Type2では機能しません。パラメーターとして使用できる実際の型が必要だからです。

3

関数に渡すと、Voidは空のパラメーターとして解釈されます。結局のところ、voidポインターを使用していません

void func (void)

なる

void func ()
0
count0