从可变参数模板实例化调用函数时调用意外的重载

Gri*_*wes 10 c++ templates forwarding variadic-templates

假设以下代码:

#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);
}
Run Code Online (Sandbox Code Playgroud)

输出是:

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]
Run Code Online (Sandbox Code Playgroud)

最后一个电话- foo(a),这里achar[4]-要求我期待的版本- template<unsigned N> void foo(const char (&)[N]).但为什么不实例化变量模板的foo调用foo(const char (&)[N],而是调用foo(const char *)呢?如果没有char数组重载,那应该是预期的 - 但为什么会在这里发生呢?不应该const First &正确捕获数组类型?

另外,使用传递给它的数组使通用可变参数版本正常工作的最简单方法是什么?


正如Matthieu M.在评论中注意到的那样,问题可能不是由可变参数模板引起的,而是由间接引起的:

#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);
}
Run Code Online (Sandbox Code Playgroud)
    char(&)[N]
    const char *

他还说它可能是编译器错误 - 虽然代码在Clang 3.2 dev,G ++ 4.6和4.7中产生完全相同的结果.

R. Martinho Fernandes指出,改变a最后一个片段的类型const char a[]会使代码产生const char *两次.

Die*_*ühl 5

我想我可以回答第二部分:如何修复它?我不确定我是否得到了正确的答案,因为在我看来,传递字符串文字的情况仍然是错误的结果.修复的基本思想是使用完美转发而不是希望推导出正确的类型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);
}
Run Code Online (Sandbox Code Playgroud)