【问题标题】:Template argument deduction fails模板参数推导失败
【发布时间】:2014-03-25 14:51:07
【问题描述】:

我正在尝试使用标签和enable_if 对模板参数实施约束。代码如下:

#include <type_traits>
#include <iostream>

template<typename Type>
struct tag {}; 

struct Atag {}; 
struct Btag {};

template<typename Type, typename Tag>
struct tag_enabled
{
    static_assert(
        std::is_same
        <
            typename tag<Type>::type, 
            Tag 
        >::value, 
        "Error: Type is not tagged with Tag."
    ); 

    typedef typename std::enable_if
    <
        std::is_same
        <
            typename tag<Type>::type, 
            Tag 
        >::value, 
        Type
    >::type type; 
};

template<typename A>
typename tag_enabled<A, Atag>::type
worker(
   typename tag_enabled<A, Atag>::type const & a
)
{
    A result; 

    std::cout << "Atag -> Atag" << std::endl;

    return result; 
}

template<typename A, typename B>
typename tag_enabled<A, Atag>::type
worker(
    typename tag_enabled<B, Btag>::type const & b
)
{
    A result; 

    std::cout << "Btag -> Atag" << std::endl;

    return result; 
}


template<typename A, typename ... Args>
A caller(Args ... args)
{
    return worker<A>(args ...);
}

struct test_a {}; 
struct test_b {}; 

template<>
struct tag<test_a>
{
    typedef Atag type; 
};

template<>
struct tag<test_b>
{
    typedef Btag type;
};

int main(int argc, const char *argv[])
{
    // caller int(int)
    test_a ta1; 
    test_b tb1; 

    auto ta2 = caller<test_a>(ta1); 

    // Why does this fail?  
    auto ta3 = caller<test_a>(tb1);

    return 0;
}

它会导致以下错误:

test-template.cpp: In instantiation of ‘A caller(Args ...) [with A = test_a; Args = {test_b}]’:
test-template.cpp:90:34:   required from here
test-template.cpp:63:30: error: no matching function for call to ‘worker(test_b&)’
     return worker<A>(args ...);
                              ^
test-template.cpp:63:30: note: candidates are:
test-template.cpp:35:1: note: template<class A> typename tag_enabled<A, Atag>::type worker(const typename tag_enabled<A, Atag>::type&)
 worker(
 ^
test-template.cpp:35:1: note:   template argument deduction/substitution failed:
test-template.cpp:63:30: note:   cannot convert ‘args#0’ (type ‘test_b’) to type ‘const type& {aka const test_a&}’
     return worker<A>(args ...);
                              ^
test-template.cpp:48:1: note: template<class A, class B> typename tag_enabled<A, Atag>::type worker(const typename tag_enabled<B, Btag>::type&)
 worker(
 ^
test-template.cpp:48:1: note:   template argument deduction/substitution failed:
test-template.cpp:63:30: note:   couldn't deduce template parameter ‘B’
     return worker<A>(args ...);

除了最后一个错误之外的所有错误都是预期和欢迎的。 tag_enabled 应确保不会根据模板参数标签实例化函数模板。这个错误例如:

test-template.cpp:35:1: note:   template argument deduction/substitution failed:
    test-template.cpp:63:30: note:   cannot convert ‘args#0’ (type ‘test_b’) to type ‘const type& {aka const test_a&}’
         return worker<A>(args ...);

很棒,因为我希望该函数的推导失败,因为它应该执行映射 Atag -&gt; Atag 而不是 Btag -&gt; Atag。如果双参数函数模板可以工作,SFINAE 会(至少我希望如此)放弃这个候选函数。这是我关心的错误,为什么模板参数推导在这里失败:

test-template.cpp:48:1: note:   template argument deduction/substitution failed:
test-template.cpp:63:30: note:   couldn't deduce template parameter ‘B’
     return worker<A>(args ...);

?

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    编译器可以从模板函数参数中推导出一个类型模板参数 AB 和一个非类型模板参数 N,该模板函数参数的类型由以下结构组成 (Stroustrup 23.5 .2,iso 14.8.2.1):

    A
    const A
    volatile A
    A*
    A&
    A[constant_expression]
    type[N]
    class_template<A>
    class_template<N>
    B<A>
    A<N>
    A<>
    A type::*
    A A::*
    type A::*
    A (*)(args)
    type (A::*)(args)
    A (type::*)(args)
    type (type::*)(args_AN)
    A (A::*)(args_AN)
    type (A::*)(args_AN)
    A (type::*)(args_AN)
    type (*)(args_AN)
    

    其中args 是一个不允许推导的参数列表,args_AN 是一个参数列表,通过递归应用上述规则,可以从中确定AN。如果不是所有的参数都可以这样推导出来,那么调用就是模棱两可的。

    你的构造

    typename tag_enabled<B, Btag>::type const &
    

    不具备上述形式之一,因此无法推导出B

    template<typename A, typename B>
    typename tag_enabled<A, Atag>::type
    worker(typename tag_enabled<B, Btag>::type const & b)
    

    B 必须明确指定,就像std::forward 的情况一样。不幸的是,这很不方便。在允许扣除的同时,有几种方便的方法是

    template<typename A, typename B, typename = typename std::enable_if <...> >
    typename tag_enabled<A, Atag>::type
    worker(B const& b)
    

    template<typename A, typename B>
    typename tag_enabled<A, Atag, B, Btag>::type
    worker(B const& b)
    

    无论哪种方式,你都必须稍微改变你的设计。

    【讨论】:

    • B 被标记为Btag 然后外部const 限定符被ADL 剥离时,tag_enabled 是否实际上不会导致B const &amp;,从而导致B&amp; 是在您在答案中发布的表格中?
    • @tmaric 我可以,但编译器无法在typename tag_enabled&lt;B, Btag&gt;::type推断 B,因为这不是上述形式之一。如果B给定的,那么是的,编译器会如您所说的那样生成B const &amp;
    • @tmaric 换一种方式想一想:我可以写tag_enabled,而typename tag_enabled&lt;B, Btag&gt;::type 会导致X whatever B 是。那么,给定XB 会是什么?这是反转(类型)函数的问题:该函数必须是可逆的。编译器不知道。
    • 谢谢!我正在阅读模板书,似乎其原因是参数推导是自上而下的方法:“匹配过程从顶层构造开始并通过组成元素递归”......跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-26
    • 2012-12-06
    • 2020-06-05
    • 1970-01-01
    • 2018-04-28
    相关资源
    最近更新 更多