对于曾经在gcc5中工作的情况,无法在gcc6的部分专业化中推导模板参数

Ale*_*kiy 5 c++ gcc templates language-lawyer

这段代码在gcc6中导致错误(但在gcc 4.8、5.2和clang 3.6中可以正常工作):

template <typename T>
struct outer
{
    template <typename U>
    struct inner
    {

    };
};


template <typename T>
struct is_inner_for
{

    template <typename Whatever>
    struct predicate
    {
        static constexpr bool value = false;
    };

    template <typename U>
    struct predicate<typename outer<T>::template inner<U>>
    {
        static constexpr bool value = true;
    };
};

static_assert(
    is_inner_for<int>::template predicate<
        outer<int>::inner<double>
    >::value,
    "Yay!"
);
Run Code Online (Sandbox Code Playgroud)

错误是:

main.cpp:22:9: error: template parameters not deducible in partial specialization:
  struct predicate<typename outer<T>::template inner<U>> : std::true_type
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:22:9: note:         'U'
 ^~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

命令行是:

g++ -std=c++1y -c main.cpp
Run Code Online (Sandbox Code Playgroud)

在此处查看Godbolt输出

我已经在这里向gcc提交了错误报告:https//gcc.gnu.org/bugzilla/show_bug.cgi? id = 70141

但是,它被标记为无效(我认为是错误的)。outer<T>内部使用的当时是predicate一种具体类型,因此它不是非推论上下文。

标准中是否有任何阻止它成为有效C ++代码的内容?

Bar*_*rry 3

我怀疑这是 gcc 6.0 中的一个错误,以及 clang 3.9 中的错误警告(该警告很奇怪 - 因为该警告意味着不会选择部分专业化,但如果不选择它,则会触发静态断言)。

\n\n
\n\n

来自[temp.class.spec.match]:

\n\n
\n

如果可以从实际模板参数列表推导出部分特化的模板参数,则部分特化与给定的实际模板参数列表匹配\n

\n
\n\n

我们可以从U中推导出来吗?typename outer<T>::template inner<U>outer<int>::inner<double>

\n\n

来自 [temp.deduct.type]:

\n\n
\n

如果模板参数仅在非推导上下文中使用并且未显式指定,则模板参数推导将失败。

\n\n

非推导的上下文是:
\n \xe2\x80\x94使用Qualified-id指定的类型的嵌套名称说明符。\n \xe2\x80\x94 [...]

\n
\n\n

但这里指定的嵌套名称typename outer<T>是,它不包含我们试图推断的类型。其他非推导上下文均不适用。所以这里推演应该成功。

\n\n

考虑以下等效情况:

\n\n
#include <utility>\n\ntemplate <class >\nstruct outer\n{\n    template <class U> struct inner {};\n};\n\ntemplate <class T>\nstruct bar {\n    template <class U> std::false_type foo(U const&);\n    template <class U> std::true_type foo(typename outer<T>::template inner<U> const&);\n};\n\n\nint main() {\n    static_assert(decltype(bar<int>{}.foo(outer<int>::inner<double>{}))::value, "!");\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

gcc 6.0 和 clang 3.9 都会在没有警告的情况下编译此代码 - 但这与原始示例中的部分特化中发生的推论是相同的。

\n