Шаблонный ребенок не принимается в качестве родителя при использовании варианта - PullRequest
3 голосов
/ 11 июня 2019

Я пытаюсь выполнить очень простую абстрактную фабрику с шаблонизированными аргументами и получаю сообщение об ошибке, до которого не могу добраться.

Основная проблема заключается в том, что за пределами шаблона вариантный тип принимает любого из членов своего объединения, но внутри шаблона это выведение кажется ошибочным. Я не могу найти ошибка в моей конструкции, которая приводит к этому.

Минимальный производящий код выглядит следующим образом; просто удаляя шаблоны, он прекрасно компилируется

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

Я считаю, что проблема не является проблемой lvalue / rvalue, подобной этому вопросу , поскольку метод, на который ссылаются, является статическим (но мы можем вытащить его и заставить его быть lvalue, не решая это ошибка тоже). GCC, как известно, имеет некоторые ошибки с псевдонимами , но я не понимаю, как они здесь будут актуальны, так как я думаю, что они изолированы от variadics; Я использую g ++ - 7.1, -std = c ++ 17

Почему компилятор не может принимать тип A там, где запрашивается тип variant<A,B>? Не в этом ли смысл вариантов? Например, AB ab = A(); или AB ab = B(); оба допустимы, хотя это может быть просто умной перегрузкой оператора =?

Я неправильно использую вариант, std :: function, templates, aliases или все вышеперечисленное?


В качестве доказательства того, что проблема связана с шаблонами, код без шаблонов компилируется как:

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();

оба кажутся приемлемыми.

1 Ответ

2 голосов
/ 11 июня 2019

Деобфускация вашего кода Я думаю, что вы пытаетесь сделать сводится к попытке следующего:

Работает

#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;

}

ошибка: неправильное преобразование из 'AB * () ()' {aka 'std :: variable () ()'} в 'Fn' {aka 'A (*) () '} [-fpermissive]

, который, если вы хотите уменьшить его, пытается преобразовать std::variant<A,B> * в A*, который не будет работать.

На самом деле вы даже не можете конвертировать A* в std::variant<A,B> *.Единственное допустимое преобразование: A в std::variant<A,B> или B в std::variant<A,B>

https://godbolt.org/z/Xwbsou

...