未能在GCC中正确推断出Variadic Type和非Type Template Template Packs的混合

时间:2017-05-17 19:14:47

标签: c++ c++11 gcc c++14 variadic-templates

好的伙计们,我试图尽量减少以下代码来说明我遇到的问题。它看起来很讨厌,省略了许多原始代码。虽然不一定与我遇到的问题相关,但我试图实现依赖注入的机制。我试图设计它,使得从已经注入依赖的类派生的类将能够从它们各自的父类继承依赖项。如果需要,我可以提供完整的代码,但这个更小的版本足以产生问题。

#include <tuple>
#include <type_traits>

template<typename ... Dependencies>
class DependencyInjectable
{
public:

    typedef DependencyInjectable<Dependencies ...> tDependencies;
    typedef tDependencies tDependencyInjectable;
    typedef std::tuple<Dependencies ...> tTuple;
    static const constexpr size_t Depth = 0;

    template<typename Arg, typename DecayArg = typename std::decay<Arg>::type, typename ... Args,
             typename = std::enable_if_t<!std::is_same<DecayArg, bool>::value>>
    DependencyInjectable(Arg &&arg, Args && ... args) { }

    virtual ~DependencyInjectable(void) { }
};

template<typename T> struct tuple_as_dependency_injectable_type { };
template<typename ... Args> struct tuple_as_dependency_injectable_type<std::tuple<Args ...>>
{
    typedef DependencyInjectable<Args ...> type;
};

template<typename T> struct dependencies_of { typedef T type; };

template<typename Dependency, typename ... Dependencies>
class DependencyInjectable<dependencies_of<Dependency>, Dependencies ...>
    : virtual public Dependency::tDependencyInjectable,
    public tuple_as_dependency_injectable_type<decltype(std::tuple_cat(std::declval<
                                                        typename Dependency::tDependencyInjectable::tTuple>(),
                                                        std::declval<std::tuple<Dependencies ...>>()))>::type
{
public:

    template<typename Arg, bool = Arg::Depth != 0, typename ... Args>
    struct get_virtual_bases
    {
        typedef typename Arg::tBase T;
        typedef typename get_virtual_bases<T, T::Depth != 0,
            typename Arg::tDependencyInjectable, Args ...>::type type;
    };

    template<typename Arg, typename ... Args>
    struct get_virtual_bases<Arg, false, Args ...>
    {
        typedef std::tuple<typename Arg::tDependencyInjectable, Args ...> type;
    };

    typedef decltype(std::tuple_cat(std::declval<typename Dependency::tDependencyInjectable::tTuple>(),
                     std::declval<std::tuple<Dependencies ...>>())) tTuple;
    typedef typename tuple_as_dependency_injectable_type<tTuple>::type tDependencies;
    typedef DependencyInjectable<dependencies_of<Dependency>, Dependencies ...> tDependencyInjectable;
    typedef Dependency tBase;
    static const constexpr auto Depth = 1 + std::tuple_size<typename get_virtual_bases<tBase>::type>::value;

    template<typename Arg, typename ... Args, typename std::enable_if<
        !std::is_same<typename std::decay<Arg>::type, decltype(std::make_index_sequence<Depth - 1>{ })>::value, int>::type = 0>
        DependencyInjectable(Arg &&arg, Args && ... args)
        : DependencyInjectable(std::make_index_sequence<Depth - 1>{ }, std::make_index_sequence<1 + sizeof ... (Args)> { },
                               std::forward_as_tuple(arg, std::forward<Args>(args) ...)) { }

private:

    template<typename ... Args, size_t ... IndicesOne, size_t ... IndicesTwo>
    DependencyInjectable(const std::index_sequence<IndicesOne ...> &, const std::index_sequence<IndicesTwo ...> &,
                         const std::tuple<Args ...> &tuple)
        : std::tuple_element<IndicesOne, typename get_virtual_bases<
        typename Dependency::tDependencyInjectable>::type>::type(std::get<IndicesTwo>(tuple) ...) ...,
        tDependencies(std::get<IndicesTwo>(tuple) ...) { }

public:

    virtual ~DependencyInjectable(void) { }
};

以下是测试类的代码段:

struct DependencyOne { };
struct DependencyTwo { };

class DependentClassOne
: virtual public DependencyInjectable<DependencyOne *>
{
public:

    DependentClassOne(const tDependencies &dependencies) : tDependencyInjectable(dependencies) { }
    virtual ~DependentClassOne(void) { }
};

class DependentClassTwo
: public DependentClassOne,
  virtual public DependencyInjectable<dependencies_of<DependentClassOne>, DependencyTwo *>
{
public:
    DependentClassTwo(const tDependencies &dependencies)
    : tBase(dependencies),
      tBase::DependencyInjectable(dependencies),
      tDependencyInjectable(dependencies) { }

    virtual ~DependentClassTwo(void) { }
};

int main(int argc, char **argv)
{
    DependencyOne dependencyOne;
    DependencyTwo dependencyTwo;

    auto &&tuple = std::make_tuple(&dependencyOne, &dependencyTwo);

    DependentClassOne dependentClassOne(tuple);
    DependentClassTwo dependentClassTwo(tuple);

    return 0;
}

使用GCC 6.3.0编译此代码会产生以下错误:

: In instantiation of 'DependencyInjectable<dependencies_of<Dependency>, Dependencies ...>::DependencyInjectable(std::index_sequence<IndicesOne ...>&, std::index_
sequence<IndicesTwo ...>&, const std::tuple<_Args1 ...>&) [with Args = {const DependencyInjectable<DependencyOne*, DependencyTwo*>&}; long long unsigned int ...IndicesOne = {0ull}; long long unsigned
int ...IndicesTwo = {0ull}; Dependency = DependentClassOne; Dependencies = {DependencyTwo*}; std::index_sequence<IndicesOne ...> = std::integer_sequence<long long unsigned int, 0ull>; std::index_seque
nce<IndicesTwo ...> = std::integer_sequence<long long unsigned int, 0ull>]':
:63:88:   required from 'DependencyInjectable<dependencies_of<Dependency>, Dependencies ...>::DependencyInjectable(Arg&&, Args&& ...) [with Arg = const Dependency
Injectable<DependencyOne*, DependencyTwo*>&; Args = {}; typename std::enable_if<(! std::is_same<typename std::decay<Arg>::type, decltype (typename std::_Make_integer_sequence<long long unsigned int, (
DependencyInjectable<dependencies_of<Dependency>, Dependencies ...>::Depth - 1), typename std::_Build_index_tuple<(DependencyInjectable<dependencies_of<Dependency>, Dependencies ...>::Depth - 1)>::__t
ype>::__type{})>::value), int>::type <anonymous> = 0; Dependency = DependentClassOne; Dependencies = {DependencyTwo*}]'
:99:41:   required from here
:72:54: error: no matching function for call to 'DependencyInjectable<DependencyOne*>::DependencyInjectable(bool)'
         tDependencies(std::get<IndicesTwo>(tuple) ...) { }
                                                      ^
:16:5: note: candidate: template<class Arg, class DecayArg, class ... Args, class> DependencyInjectable<Dependencies>::DependencyInjectable(Arg&&, Args&& ...)
     DependencyInjectable(Arg &&arg, Args && ... args) { }
     ^~~~~~~~~~~~~~~~~~~~
:16:5: note:   template argument deduction/substitution failed:
:5:7: note: candidate: constexpr DependencyInjectable<DependencyOne*>::DependencyInjectable(const DependencyInjectable<DependencyOne*>&)
 class DependencyInjectable
       ^~~~~~~~~~~~~~~~~~~~
:5:7: note:   no known conversion for argument 1 from 'bool' to 'const DependencyInjectable<DependencyOne*>&'

具体来说,我很困惑为什么GCC会发出这个错误:

:72:54:错误:没有匹配函数来调用&#39; DependencyInjectable :: DependencyInjectable(bool)&#39;          tDependencies(std :: get(tuple)...){}

显然,模板参数推断失败了,但我不确定为什么。如何推断&#39; bool&#39; ?海湾合作委员会似乎在努力解决以下问题:

typename Dependency::tDependencyInjectable>::type>::type(std::get<IndicesTwo>(tuple) ...) ...,

我故意在基类中添加以下构造函数来捕获问题:

template<typename Arg, typename DecayArg = typename std::decay<Arg>::type, typename ... Args,
         typename = std::enable_if_t<!std::is_same<DecayArg, bool>::value>>
DependencyInjectable(Arg &&arg, Args && ... args) { }

此代码由Microsoft Visual Studio 14接受并按预期运行。稍微不同的版本与Clang编译,虽然这个特定版本可能没有。 Clang和Visual Studio都是正确的,或GCC正确拒绝我的代码。我错过了什么?

0 个答案:

没有答案