如何在基类中使用unordered_map并使用奇怪的重复模板模式?

时间:2016-03-25 22:04:33

标签: c++ templates c++11

我正在使用curiously recurring template pattern

#include <unordered_map>

using namespace std;

template<class S>
struct State {
    unordered_map<int, S> children; // <-- problem!
};

struct TicTacToeState : public State<TicTacToeState> {
    // implementation
};

int main() {
    return 0;
}

基本上,我希望在(基本)状态下拥有子状态的无序映射。

使用g++ file.cpp -std=c++11编译:

In file included from /usr/include/c++/4.8/utility:70:0,
                 from /usr/include/boost/config/no_tr1/utility.hpp:21,
                 from /usr/include/boost/config/select_stdlib_config.hpp:37,
                 from /usr/include/boost/config.hpp:40,
                 from /usr/include/boost/functional/hash/hash_fwd.hpp:17,
                 from /usr/include/boost/functional/hash/hash.hpp:13,
                 from /usr/include/boost/functional/hash.hpp:6,
                 from tests/../examples/tic_tac_toe.cpp:1,
                 from tests/test_tic_tac_toe.cpp:3:
/usr/include/c++/4.8/bits/stl_pair.h: In instantiation of ‘struct std::pair<const long unsigned int, TicTacToeState>’:
/usr/include/c++/4.8/type_traits:615:28:   required from ‘struct std::__is_destructible_impl<std::pair<const long unsigned int, TicTacToeState> >’
/usr/include/c++/4.8/type_traits:637:12:   required from ‘struct std::__is_destructible_safe<std::pair<const long unsigned int, TicTacToeState>, false, false>’
/usr/include/c++/4.8/type_traits:652:12:   required from ‘struct std::is_destructible<std::pair<const long unsigned int, TicTacToeState> >’
/usr/include/c++/4.8/type_traits:116:12:   required from ‘struct std::__and_<std::is_destructible<std::pair<const long unsigned int, TicTacToeState> >, std::__is_direct_constructible_impl<std::pair<const long unsigned int, TicTacToeState>, const std::pair<const long unsigned int, TicTacToeState>&> >’
/usr/include/c++/4.8/type_traits:817:12:   required from ‘struct std::__is_direct_constructible_new_safe<std::pair<const long unsigned int, TicTacToeState>, const std::pair<const long unsigned int, TicTacToeState>&>’
/usr/include/c++/4.8/type_traits:895:12:   [ skipping 5 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/include/c++/4.8/type_traits:974:12:   required from ‘struct std::is_copy_constructible<std::pair<const long unsigned int, TicTacToeState> >’
/usr/include/c++/4.8/bits/alloc_traits.h:540:12:   required from ‘struct std::__is_copy_insertable<std::allocator<std::pair<const long unsigned int, TicTacToeState> > >’
/usr/include/c++/4.8/bits/alloc_traits.h:560:63:   required by substitution of ‘template<class _Alloc> using __check_copy_constructible = std::__allow_copy_cons<std::__is_copy_insertable<_Alloc>::value> [with _Alloc = std::allocator<std::pair<const long unsigned int, TicTacToeState> >]’
/usr/include/c++/4.8/bits/unordered_map.h:97:11:   required from ‘class std::unordered_map<long unsigned int, TicTacToeState, std::hash<long unsigned int>, std::equal_to<long unsigned int>, std::allocator<std::pair<const long unsigned int, TicTacToeState> > >’
tests/../examples/../gtsa.hpp:110:30:   required from ‘struct State<TicTacToeState, TicTacToeMove>’
tests/../examples/tic_tac_toe.cpp:69:32:   required from here
/usr/include/c++/4.8/bits/stl_pair.h:102:11: error: ‘std::pair<_T1, _T2>::second’ has incomplete type
       _T2 second;                /// @c second is a copy of the second object
           ^
In file included from tests/test_tic_tac_toe.cpp:3:0:
tests/../examples/tic_tac_toe.cpp:69:8: error: forward declaration of ‘struct TicTacToeState’
 struct TicTacToeState : public State<TicTacToeState, TicTacToeMove> {
        ^
In file included from /usr/include/c++/4.8/utility:70:0,
                 from /usr/include/boost/config/no_tr1/utility.hpp:21,
                 from /usr/include/boost/config/select_stdlib_config.hpp:37,
                 from /usr/include/boost/config.hpp:40,
                 from /usr/include/boost/functional/hash/hash_fwd.hpp:17,
                 from /usr/include/boost/functional/hash/hash.hpp:13,
                 from /usr/include/boost/functional/hash.hpp:6,
                 from tests/../examples/tic_tac_toe.cpp:1,
                 from tests/test_tic_tac_toe.cpp:3:
/usr/include/c++/4.8/bits/stl_pair.h: In instantiation of ‘constexpr std::pair<_T1, _T2>::pair(_U1&&, _U2&&) [with _U1 = long unsigned int&; _U2 = TicTacToeState&; <template-parameter-2-3> = void; _T1 = const long unsigned int; _T2 = TicTacToeState]’:
tests/../examples/../gtsa.hpp:640:57:   required from ‘S* MonteCarloTreeSearch<S, M>::add_child(S*, M&) [with S = TicTacToeState; M = TicTacToeMove]’
tests/../examples/../gtsa.hpp:472:41:   required from ‘S* MonteCarloTreeSearch<S, M>::tree_policy(S*, S*) [with S = TicTacToeState; M = TicTacToeMove]’
tests/../examples/../gtsa.hpp:453:44:   required from ‘void MonteCarloTreeSearch<S, M>::monte_carlo_tree_search(S*) [with S = TicTacToeState; M = TicTacToeMove]’
tests/../examples/../gtsa.hpp:433:41:   required from ‘M MonteCarloTreeSearch<S, M>::get_move(S*) [with S = TicTacToeState; M = TicTacToeMove]’
tests/test_tic_tac_toe.cpp:123:1:   required from here
/usr/include/c++/4.8/bits/stl_pair.h:145:64: error: using invalid field ‘std::pair<_T1, _T2>::second’
  : first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y)) { }
                                                                ^

我的理解是:

我要求创建一个结构TicTacToeState,它看起来是什么基类,啊哈,它的模板,很好,它试图用S = TicTacToeState实例化状态但是失败了,因为unordered_map<int, S>需要已为地图值定义类型,但未定义TicTacToeState。

将TicTacToeState放在前面不会有帮助,因为那时它将没有国家声明。

如果有人希望更广泛地查看原始代码,则定义State here。我是第一次使用CRTP,也许我不需要它,它只会使代码复杂化?我不知道,我还没有找到更简单的东西。

如何让它发挥作用?

1 个答案:

答案 0 :(得分:2)

类/结构体不能具有不完整类型的成员,因此它们不能引用自身,因为它们需要自己的定义来形成这个类。

由于在std :: unordered_map上循环应该为您提供std::pair<const Key, Value>的引用,它需要一个完整的类型。

因此,为了做这样的事情,你必须使用一个使用分配的包装类,允许你首先完成类型并在其他地方使用它。 在大多数情况下,我会使用std::unordered_map<int, std::unique_ptr<S>>,但如果你真的需要创建元素,你可以将它包装起来。

struct SWrapper final
{
    SWrapper();
    operator S&() { return *_S; }
    operator const S&() const { return *_S; }
private:
    std::unique_ptr<S> _S;
};

// In .cpp
SWrapper::SWrapper() : _S(std::make_unique<S>()) {}