为什么const在这个模板结构中丢失了?

时间:2017-12-14 12:45:47

标签: c++ templates language-lawyer

众所周知,以下函数指针具有不同的类型:

void foo_int_ref(int&);
void foo_const_int_ref(const int&);

static_assert(
    !std::is_same<
        decltype(foo_int_ref),
        decltype(foo_const_int_ref)
    >::value,
    "Types should be different");

让我们考虑一下这个概括:

template<typename T>
void foo(T t) {}

template<typename T>
struct ref_vs_const_ref {
    typedef decltype(foo<T>) foo_T;
    typedef decltype(foo<const T>) foo_const_T;
};

using int_ref_vs_const_ref = ref_vs_const_ref<int&>;

static_assert(
    !std::is_same<
        typename int_ref_vs_const_ref::foo_T,
        typename int_ref_vs_const_ref::foo_const_T
    >::value,
    "Types should be different"); // -- it fails

最后一个断言失败了。出于某种原因,const遗失了foo_const_T。但为什么呢?

2 个答案:

答案 0 :(得分:5)

const值参数会影响任何形状或形式的签名。从非模板声明中删除引用时也是如此。 const仅影响函数定义中参数的使用。如果添加引用或指向类型的指针,则更改内容,const会影响函数的类型。

在嵌套类型中,T是应用了int&的{​​{1}}。但是,constT&也是相同的类型。我想您的混淆源于您在{左侧T& const的不明智的位置:const更适用于顶级实体。

答案 1 :(得分:1)

已经给出了“为什么”这种情况发生的答案。这是解决问题的可能方法 - 将“const”添加到嵌套类型:

template<typename T, typename = void>
struct add_value_const
{
    using type = const T;
};

template<typename T>
using add_value_const_t = typename add_value_const<T>::type;

template<typename T>
struct add_value_const<
    T, std::enable_if_t<
        std::is_reference<T>::value
    >
>
{
    using type = std::conditional_t<
        std::is_lvalue_reference<T>::value,
        add_value_const_t<std::remove_reference_t<T>>&,
        add_value_const_t<std::remove_reference_t<T>>&&
    >;
};

template<typename T>
struct add_value_const<
    T, std::enable_if_t<std::is_pointer<T>::value>
>
{
    using type = add_value_const_t<std::remove_pointer_t<T>>*;
};

在您的情况下,您必须使用foo<add_value_const<T>>而不是foo<const T>

我使用了C ++ 14语法,但它可以轻松移植到C ++ 11。使用C ++ 17,它更具可读性。

您可以找到编译示例on godbolt