嵌套类构造函数中的父模板参数推导

时间:2019-01-09 18:34:10

标签: c++ c++11 constructor inner-classes template-deduction

我正在尝试编写嵌套类的“ promotion”构造函数,以推断出父类模板。它适用于父类,但不适用于嵌套类。这是一个代码示例。

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 :(得分:2)

我不知道为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!
}

答案 1 :(得分:1)

不能从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;
        };
        // ...
    };