在嵌套模板结构中将引用类型作为模板参数传递不起作用

Passing reference type as template argument in nested template structure does not work

我很困惑,我正在设计一个模板,我发现了一个关于用 T=float&:

实例化模板的奇怪行为
// Given an available float f:
float f = 1.0;

// This getter works for T=float&:
template <typename T>
struct test {
  const T get() { return f; }
};

int main() {
  float& f1 = test<float&>().get();
}

第一个奇怪的事情是 f1 应该是 const float& 才能使代码正确,因此,我预计会出现错误,但它工作正常。

第二个奇怪的事情是在这个类似的例子中,当我不希望它报告错误时:

// Given an available float f:
float f = 1.0;

struct State {
  const float& get() { return f; }
};

// This does not work for T=float&:
template <typename T>
struct test2 {
  State state;
  const T get() { return state.get(); }
};

int main() {
  const float& f2 = test2<float&>().get();
}

报错是这样的:

main.cpp: In instantiation of 'const T test2<T>::get() [with T = float&]':
main.cpp:31:41:   required from here
main.cpp:22:36: error: binding reference of type 'float&' to 'const float' discards qualifiers
   const T get() { return state.get(); }

这很奇怪,因为第二个示例只声明了 const float& 类型,而不是 float&const float,所以我不知道发生了什么。

也许模板不是为引用而设计的,或者它是 GCC 上的一个错误,或者我只是在做一些愚蠢的事情。

我使用 gcc (GCC) 6.3.1 20170306 测试了这段代码,还在 repl.it 网站上使用 C++11 进行了测试。

此外,如果它是一个错误,我会对任何可用的解决方法感兴趣。

请看一下this question and this问题。

同样的事情也发生在你的案例中。

对于第一种情况,我们的类型是float&。而在 float & const 中,const 是多余的。因此它将仅解析为 float &

对于第二种情况,State::get() 返回的是对 const float 值的引用。 test2<float&>::get()返回的还是float&。现在编译器将阻止您将 const float 分配给非常量。

只是更专业一点。我认为这给出了你想要的行为:

template <typename T>
struct test {
    T get() { return f; }
};

template <typename T>
struct test<T&> {
    const T& get() { return f; }
};

测试:

int main() {      
    const float& f1 = test<float&>().get();
    float& f2 = test<float&>().get(); //Error
    const float& f3 = test<const float&>().get();
    float f4 = std::move(test<float>().get());
}

为了完整起见,以下是我可能会采用的解决方案:

template <class T>
struct make_non_modifiable { using type = const T; };
template <class T>
struct make_non_modifiable<T&> { using type = const T &; };
template <class T>
struct make_non_modifiable<T*> { using type = const T *; };

template <typename T>
struct test {
  typename make_non_modifiable<T>::type get() { return f; }
};

我没有尝试编译这段代码,所以可能会有一两个错字。
我通常更喜欢 class 模板部分特化而不是函数模板特化,因为后者与函数重载一起更难理解。