使用变体时,模板化 child 不接受为 parent

Templatized child not accepted as parent when using variant

我正在尝试使用模板化参数执行一个非常简单的抽象工厂,但收到一个我无法查明的错误。

核心问题是,在模板之外,变体类型接受其联合的任何成员,但在模板内部,这种推导似乎失败了。 我在构造中找不到导致这种情况发生的错误。

最小生成代码如下;只需删除模板,它就可以正常编译

class A{};
class B{};

using AB = std::variant<A,B>;

template <class T1, class T2>
class Parent
{
};

class Child : public Parent<A,B>
{
public:
    static Parent<A,B>* build(){ return new Child(); }
};


template <class T1, class T2>
using Fn = std::function<Parent<T1,T2> *(void)>;

using ParentFn = Fn<AB,AB>;

int main()
{
        // in a factory, we might say
        // map<string, ParentFn> m; m["key"] = &Child::build;
        // but for MRE I'll just do assignment
        ParentFn tmp = &Child::build; // does just fine without templates, crashes horribly with

    return 0;
}

然而,正如所写,它吐出编译错误

main.cpp:32:24: error: conversion from ‘Parent<A, B>* (*)()’ to non-scalar type ‘ParentFn {aka std::function<Parent<std::variant<A, B>, std::variant<A, B> >*()>}’ requested
         ParentFn tmp = &Child::build; // does just fine without templates, crashes horribly with

我认为问题不在于 an lvalue/rvalue issue like this question, since the method being referenced is static (but we can pull it out and force it to be an lvalue without resolving this error, too). GCC is known to have some bugs with aliases,但我不明白它们在这里有何相关性,因为我认为它们与可变参数无关;我正在使用 g++-7.1,-std=c++17

为什么编译器无法在请求类型 variant<A,B> 的地方接受类型 A?这不是变体的全部意义吗?例如,AB ab = A();AB ab = B(); 都是有效的,尽管这可能只是 = 运算符的巧妙重载?

我是否在滥用变体、std::function、模板、别名或以上所有内容?


为了证明问题与模板有关,没有模板的代码编译为:

class Parent
{
};

class Child : public Parent
{
public:
    static Parent* build(){ return new Child(); }
};

using Fn = std::function<Parent *(void)>;

using ParentFn = Fn;

int main()
{
        ParentFn tmp = &Child::build; // does just fine without templates, crashes horribly with

    return 0;
}

我相信 std 变体可以替代任何一种类型的依据是简单的测试

AB ab1 = A();
ab1 = B();

两者似乎都可以接受。

对您的代码进行反混淆我认为您要尝试做的事情归结为尝试以​​下操作。

有效

#include <variant>

class A{};
class B{};

using AB = std::variant<A,B>;


AB * Foo();

typedef AB*(*Fn)();

int main()
{
    Fn f = &Foo;

}

不起作用

#include <variant>

class A{};
class B{};

using AB = std::variant<A,B>;


AB * Foo();

typedef A*(*Fn)();

int main()
{
    Fn f = &Foo;

}

error: invalid conversion from 'AB* ()()' {aka 'std::variant ()()'} to 'Fn' {aka 'A (*)()'} [-fpermissive]

如果您想进一步减少它,则试图将 std::variant<A,B> * 转换为 A*,但这是行不通的。

事实上,您甚至无法将 A* 转换为 std::variant<A,B> *。唯一允许的转换是 Astd::variant<A,B>Bstd::variant<A,B>

https://godbolt.org/z/Xwbsou