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

时间:2017-06-02 17:36:08

标签: c++ c++11 templates gcc pass-by-reference

我很困惑,我正在设计一个模板,我发现了一个奇怪的行为,用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 20170306repl.it网站使用C ++ 11测试了此代码。

如果它是一个错误,我会对任何可用的工作感兴趣。

3 个答案:

答案 0 :(得分:1)

请查看this问题和this问题。

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

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

对于第二种情况,State::get()返回的是对const float值的引用。 test2<float&>::get()返回的那个仍然是float&。现在编译器会阻止你将const float分配给非const的。

答案 1 :(得分:1)

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

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());
}

答案 2 :(得分:1)

为了一些完整性,这是我可能会采用的解决方案:

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; }
};

我没有尝试编译此代码,因此可能会有一两个错误。
我通常更喜欢类模板部分特化而不是函数模板特化,因为后者在函数重载时更难理解。