Вывод аргумента родительского шаблона в конструкторе вложенного класса - PullRequest
0 голосов
/ 09 января 2019

Я пытаюсь написать конструктор «раскрутки» для вложенного класса, который может определить шаблон родительского класса. Он отлично работает для родительского класса, но не для вложенного класса. Вот пример кода.

template <class T>
struct potato {
    struct baked {
        template <class O>
        baked(const typename potato<O>::baked& p)
                : something(static_cast<T>(p.something)) {
        }
        baked() = default;

        T something;
    };

    template <class O>
    potato(const potato<O>& p)
            : mybaked(p.mybaked) {
    }
    potato() = default;

    baked mybaked;
};

int main(int, char**) {
    potato<int> potato1;
    potato<short> potato2(potato1);
}

Это законно?

Различные компиляторы выдают различные ошибки. Clang имеет наиболее читаемый в моей голове. В нем говорится:

шаблон кандидата проигнорирован: невозможно определить аргумент шаблона 'O'

https://godbolt.org/z/y_IZiE

Так что я предполагаю, что либо я испортил подпись, либо это не поддерживаемая функция c ++.

Ответы [ 2 ]

0 голосов
/ 10 января 2019

Как состояние компилятором O не выводится из const typename potato<O>::baked& (слева ::).

У вас есть несколько обходных путей:

  • Переместите baked вне родителя и сделайте его шаблоном:

    // Possibly in namespace details
    template <typename T>
    struct baked_impl {
        template <class O>
        baked_impl(const typename baked_impl<O>& p)
                : something(static_cast<T>(p.something)) {
        }
        baked_impl() = default;
    
        T something;
    };
    
    template <class T>
    struct potato {
        using baked = baked_impl<T>;
    
        // ...
    };
    
  • Добавить родительскую информацию в baked и использовать SFINAE:

    template <class T> struct potato;
    
    // traits for SFINAE
    template <class T> struct is_potato : std::false_type {};
    template <class T> struct is_potato<potato<T>> : std::true_type {};
    
    template <class T>
    struct potato {
        using value_type = T;
        struct baked {
            using parent = potato;
    
            template <class O, std::enable_if_t<is_potato<typename O::parent>::value, int> = 0>
            baked(const O& p)
                    : something(static_cast<typename O::parent::value_type>(p.something)) {
            }
            baked() = default;
    
            T something;
        };
        // ...
    };
    
0 голосов
/ 09 января 2019

Я не знаю способа вывести аргумент шаблона T для родителя baked potato<T>. Вы можете узнать T, используя decltype(p.something), но, похоже, это не поможет решить проблему с вызовом конструктора. Один из обходных путей - изменить конструктор baked на любой O и предположить, что он имеет something:

struct baked {
    template <class O>
    baked(const O & p) : something(static_cast<T>(p.something))
    { }

    baked() = default;

    T something;
};

Это будет работать, но это менее безопасно для типов, чем, как кажется, предполагается вашим исходным кодом. Одним из способов решения проблемы , в которой проблема , может быть введение static_assert, проверяющего, что O действительно potato<U>::baked:

#include <type_traits>

template <class T>
struct potato {
    struct baked {
        template <class O>
        baked(const O & p) : something(static_cast<T>(p.something))
        {
            using t_parent = potato<decltype(p.something)>;
            static_assert(std::is_same<O, typename t_parent::baked>::value, "Not a baked potato!");
        }

        baked() = default;

        T something;
    };

    template <class O>
    potato(const potato<O>& p)
        : mybaked(p.mybaked) {
    }
    potato() = default;

    baked mybaked;
};

Это должно скомпилироваться нормально для предполагаемого использования, но не с "Не печеная картошка!" если вы попытаетесь передать что-нибудь еще с something. Это не удастся:

struct foo {
    int something = 0;
};


int main(int, char**) {
    foo bar;
    potato<int>::baked baz(bar); // Error: Not a baked potato!
}
...