模板参数推导失败

模板参数推导失败

本文介绍了模板参数推导失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用标签和 enable_if 来强制对模板参数进行约束.代码如下:

I'm trying to use tags and enable_if to enforce constraints on template parameters. Here is the code:

#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 应该根据模板参数标签确保函数模板不会被实例化.这个错误例如:

All errors but the last one are expected and welcomed. The tag_enabled should make sure that function templates do not get instantiated, based on the template parameter tags. This error for example:

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 ->Atag 而不是 Btag ->标签.如果双参数函数模板可以工作,SFINAE 会(我希望至少是这样)放弃这个候选函数.这是我关心的错误,为什么模板参数推导在这里失败:

is great because I want the deduction to fail for that function, since it's supposed to perform the mapping Atag -> Atag and not Btag -> Atag. SFINAE would (I hope so at least) just drop this function candidate, if the two-parameter function template would work. This is the error I'm concerned with, why is the template argument deduction failing here:

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 ...);

?

推荐答案

一个编译器可以推导出一个类型模板参数,AB,和一个非类型模板参数 N,来自模板函数参数,其类型由以下结构组成 (Stroustrup 23.5.2,iso 14.8.2.1):

A compiler can deduce a type template argument, A or B, and a non-type template argument, N, from a template function argument with a type composed of the following constructs (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是一个参数列表,其中一个A>N 可以通过上述规则的递归应用来确定.如果不是所有的参数都可以这样推导出来,那么调用就是不明确的.

where args is a parameter list that does not allow deduction, and args_AN is a parameter list from which an A or an N can be determined by recursive application of the above rules. If not all parameters can be deduced in this way, a call is ambiguous.

你的构造

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

不具有上述形式之一,所以不能推导出B

does not have one of the above forms, so B cannot be deduced from

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

B 必须明确指定,与 std::forward.不幸的是,这非常不方便.在允许扣除的同时使其方便的几种方法是

B has to be specified explicitly, exactly as is the case for std::forward. Unfortunately, this is quite inconvenient. A couple of ways to make it convenient while allowing deduction would be

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)

不管怎样,你都必须稍微改变你的设计.

Either way, you would have to change your design a little.

这篇关于模板参数推导失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-14 05:41