首先,我真的很抱歉这段代码的质量很差,但我已经用了1个小时来隔离我的问题来源,而且我没有比这更短的例子。 所以这是代码:
#include <iostream>
#include <type_traits>
#include <utility>
#include <tuple>
#include <array>
template <class Crtp, class... Types>
struct Base
{
template <
unsigned int Index,
class Type = typename std::tuple_element<Index, std::tuple<Types...> >::type
>
inline const Type& get() const {
return std::get<Index>(data);
}
template <
unsigned int Index,
class Type = typename std::tuple_element<Index, std::tuple<Types...> >::type
>
inline Crtp& set(const Type& value) {
std::get<Index>(data) = value; return static_cast<Crtp&>(*this);
}
std::tuple<Types...> data;
};
template <typename Type, unsigned int Size>
struct Derived : public Base<Derived<Type, Size>, std::array<Type, Size>>
{
template <
class... Args,
class Template = decltype(std::declval<const Base<
Derived<Type, Size>,
std::array<Type, Size>
>>().template get<0>(std::declval<Args>()...))
>
inline Template test(Args&&... args) const {
return this->template get<0>(std::forward<Args>(args)...);
}
template <
class... Args,
class Template = decltype(std::declval<const Base<
Derived<Type, Size>,
std::array<Type, Size>
>>().template set<0>(std::declval<Args>()...))
>
inline Derived<Type, Size>& test(Args&&... args) {
return this->template set<0>(std::forward<Args>(args)...);
}
static void check() {
Derived<double, 3> derived;
std::cout<<derived.test()[0]<<std::endl;
}
};
int main(int argc, char* argv[])
{
Derived<double, 3> derived;
std::cout<<derived.test()[0]<<std::endl; // Working
Derived<double, 3>::check(); // Not working: error: no match for ‘operator[]’ (operand types are ‘Derived<double, 3u>’ and ‘int’)
return 0;
}
说明做了什么:
有一个Base
类,它将派生类(CRTP)和元组类型作为模板参数。此基类有两个成员:get
的第tuple
个set
,tuple
的第n个元素的Derived
。Base
。
然后,有一个std::array
类继承自std::tuple<std::array<Type, Size>> data
类,并将test()
放在基类的元组中:因此,此派生类的数据类型为:{ {1}}。此派生类具有重载函数get
,根据其参数调用set
或test()
函数:get()
将调用test(std::array<double, 3>{1, 2, 3})
但set(std::array<double, 3>{1, 2, 3})
将致电test()[0]
。因此main()
应该返回数组的第一个元素:它在g++ 4.8.1
中工作,但它在静态函数中不起作用。
我不知道编译器正在尝试做什么,但显然这不起作用。 我认为这是{{1}}中的一个错误(我没有尝试过其他版本),但我想确定这一点。
所以这是我的问题:
答案 0 :(得分:1)
“编译器尝试做什么”在Derived::test
内调用时没有参数的check()
的常量和非常量重载之间错误地执行重载解析。你可以通过插入来看到
std::cout << typeid(decltype(derived.test())).name() << std::endl;
check()
(“7DerivedIdLj3EE”)和main()
(“St5arrayIdLy3EE”)。
编辑:一些调查,交替评论const / nonconst测试重载,表明模板推导不会失败test
中check()
的非常数重载。参数包Args
为空,因此替换失败应发生在decltype
表达式中,该表达式确定没有参数的set<0>
的返回类型。
当你在思考为什么时,我建议你简化代码以避免它:
#include <iostream>
#include <utility>
#include <tuple>
#include <array>
template <class Crtp, class... Types>
struct Base
{
std::tuple<Types...> data;
template <unsigned int Index>
auto get() const -> decltype(std::get<Index>(data)) {
return std::get<Index>(data);
}
template <unsigned int Index, typename T>
Crtp& set(T&& value) {
std::get<Index>(data) = std::forward<T>(value);
return static_cast<Crtp&>(*this);
}
};
template <typename Type, unsigned int Size>
struct Derived : public Base<Derived<Type, Size>, std::array<Type, Size>>
{
auto test() const -> decltype(this->template get<0>()) {
return this->template get<0>();
}
template <typename T>
Derived& test(T&& value) {
return this->template set<0>(std::forward<T>(value));
}
static void check() {
Derived<double, 3> derived;
std::cout << derived.test()[0] << std::endl;
}
};
int main(int, char*[])
{
Derived<double, 3> derived;
std::cout << derived.test()[0] << std::endl;
Derived<double, 3>::check();
}