10

次のコードを想定します。

#include <iostream>

template<typename... T>
void foo(const T &...);

template<unsigned N>
void foo(const char (&)[N])
{
   std::cout << "char(&)[N]" << std::endl;
}

void foo(const char *)
{
   std::cout << "const char *" << std::endl;
}

template<typename T>
void foo(const T &)
{
   std::cout << "Single" << std::endl;
}

template<typename First, typename... T>
void foo(const First & first, const T &... rest)
{
   std::cout << "Generic + " << sizeof...(T) << std::endl;
   foo(first);
   foo(rest...);
}

int main()
{
    const char * c = "asdf";
    char a[] = {'a', 'b', 'c', 'd'};
    foo('f', c, a, 1);
    foo(a);
}

出力は次のとおりです。

Generic + 3
Single              // fine; 'f' is `char` -> generic
Generic + 2
const char *        // fine; c is `const char *`
Generic + 1
const char *        // (!) not fine
Single
char(&)[N]          // fine; a is char[4]

最後の呼び出し -foo(a)aあるchar[4]- は、私が期待しているバージョンを呼び出します - template<unsigned N> void foo(const char (&)[N])fooしかし、 callの可変個引数テンプレートのインスタンス化ではなくfoo(const char (&)[N]foo(const char *)代わりに呼び出すのはなぜですか? char 配列のオーバーロードがなければ、それは当然のことですが、なぜここで発生しているのでしょうか? const First &配列型を適切にキャプチャするべきではありませんか?

また、渡された配列で一般的な可変個引数バージョンを適切に機能させる最も簡単な方法は何でしょうか?


Matthieu M.がコメントで気づいたように、この問題はおそらく可変個引数テンプレートではなく、indirectionによって引き起こされます。

#include <iostream>

template <unsigned N>
void foo(const char (&)[N])
{
   std::cout << "char(&)[N]" << std::endl;
}

void foo(const char *)
{
   std::cout << "const char *" << std::endl;
}

template <typename T>
void goo(T const& t) {
    foo(t);
}

int main()
{
    char a[] = {'a', 'b', 'c', 'd'};
    foo(a);
    goo(a);
}
    文字(&)[N]
    const char *

彼はまた、それはコンパイラのバグかもしれないとも言いました - コードは Clang 3.2 dev、G++ 4.6 および 4.7 の両方でまったく同じ結果をもたらしますが。

R. Martinho Fernandesaは、最後のスニペットで の型を に変更するとconst char a[]、コードがconst char *2 回生成されることに注意しました。

4

1 に答える 1

5

2 番目のセクションに答えることができると思います: どうすれば修正できますか? 私の意見では、文字列リテラルを渡す場合でも間違った結果が生じるため、正しい答えが得られたかどうかはわかりません。修正の基本的な考え方は、T const&. 配列を使用T const&すると、配列が減衰する理由はわかりませんが、よくわかりません。

もちろん、完全転送を使用するということは、それを行う関数が完全に一致し、特殊化の一部が実際に何らかの変換を行うことを意味し、少なくともconst. したがって、別の名前を使用する必要があります。全体として、これは次のようになります。

#include <iostream>
#include <utility>

void foo()
{
}

template<unsigned N>
void special(const char (&)[N])
{
    std::cout << "char(&)[" << N << "]" << '\n';
}

void special(const char *)
{
   std::cout << "const char *" << '\n';
}

template<typename T>
void special(const T &)
{
   std::cout << "Single" << '\n';
}

template<typename First, typename... T>
void foo(First&& first, T&&... rest)
{
   std::cout << "Generic + " << sizeof...(T) << '\n';
   special(std::forward<First>(first));
   foo(std::forward<T>(rest)...);
}

int main()
{
    char const* c("foo");
    char a[] = {'a', 'b', 'c', 'd'};
    foo('f', "afas", a, c, 1);
    foo(a);
}
于 2012-10-28T14:02:23.543 に答える