【问题标题】:Why is const lost in this template structure?为什么 const 在这个模板结构中丢失了?
【发布时间】:2017-12-14 12:45:47
【问题描述】:

已知以下函数指针有不同的类型:

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。但为什么呢?

【问题讨论】:

  • 当T 是引用类型时,const T 与T ([dcl.ref]/1) 相同。在const int&amp; 中,const 适用于int,而不适用于int&amp;。

标签: c++ templates language-lawyer


【解决方案1】:

值参数上的const不影响任何形状或形式的签名。当您从非模板声明中删除引用时也是如此。 const 只影响函数定义中参数的使用。如果你添加一个引用或指向类型的指针,事情会发生变化,const 会影响函数的类型。

在您的嵌套类型中,T 是应用了const 的int&amp;。但是,T&amp; 和T&amp; const 也是相同的类型。我猜您的困惑源于您不明智地将const 放置在左侧:const 更多地适用于顶级实体。

【讨论】:

  • @ivaigult - const T 不会转换为 const int&amp;,就像 T = int* 不会转换为 const int*。
  • @ivaigult: 当T 是int&amp; 时,const T 不 转换为const int&amp;,而是转换为int&amp; const。
  • int&amp; const x = y 没有事件编译,这是什么意思?
  • @AnkurSharma 是的,你不能写T&amp; const,因为引用已经是不可变的。我要说明的是const X 其中X 是T&amp; 的类型别名与const T&amp; 不同(或T const&amp; 将const 正确放置在右侧)。相反,const X 和 X 是相同的(here 是 Compiler Explorer 链接)。
【解决方案2】:

已经给出了“为什么”会发生这种情况的答案。这是解决您的问题的一种可能方法 - 将“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&lt;add_value_const&lt;T&gt;&gt; 而不是 foo&lt;const T&gt;。

我使用了 C++14 语法,但它可以很容易地移植到 C++11。使用 C++17,它的可读性更高。

你可以找到编译示例on godbolt。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-28
    • 2018-05-21
    • 2020-01-31
    • 1970-01-01
    相关资源
    最近更新 更多