在嵌套模板结构中将引用类型作为模板参数传递不起作用
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 模板部分特化而不是函数模板特化,因为后者与函数重载一起更难理解。
我很困惑,我正在设计一个模板,我发现了一个关于用 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 模板部分特化而不是函数模板特化,因为后者与函数重载一起更难理解。