显式构造函数仍允许隐式转换

时间:2018-02-24 11:49:04

标签: c++ constructor c++14 implicit-conversion explicit-constructor

我在C ++ 14中绘制了一个小的泛型类型包装模板,用于使用mixin启用,禁用或扩展基础类型的接口。

这是这个包装器的代码(剥离了一点):

namespace detail {
    /// Helper template that is forwarded to the mixins used 
    /// for the extension of wrapper<•> in order to enable 
    /// access to the actual type `Derived`
    template <typename Derived>
    struct Cast {
        using type = Derived;

        template <typename T>
        static constexpr Derived& self(T* self) { return *static_cast<Derived*>(self); }

        template <typename T>
        static constexpr Derived const& self(T const* self) { return *static_cast<Derived const*>(self); }
    };

    /// This helper template is used to derive from all the Mixins 
    /// one after another, making sure the constructor is mixed in as well:
    template <typename Cast, typename T,template <typename...> class...Mixins>
    struct wrapper_impl;

    template <typename Cast, typename T,template <typename...> class First, template <typename...> class...Rest>
    struct wrapper_impl<Cast, T, First, Rest...>
        : First<Cast, T>
        , wrapper_impl<Cast, T, Rest...>
    {
        using First<Cast, T>::First;
        using wrapper_impl<Cast, T, Rest...>::wrapper_impl;
    };

    template <typename Cast, typename T, template <typename...> class First>
    struct wrapper_impl<Cast, T, First>
        : First<Cast, T>
        , wrapper_impl<Cast, T>
    {
        using First<Cast, T>::First;
        using wrapper_impl<Cast, T>::wrapper_impl;
    };

    template <typename Cast, typename T>
    struct wrapper_impl<Cast, T>  {
    };
}


template <typename T, typename Tag, template <typename...> class...Mixins>
class wrapper : public detail::wrapper_impl<detail::Cast<wrapper<T,Tag,Mixins...>>, T, Mixins...> {
public:
    using value_type = T;
    using detail::wrapper_impl<detail::Cast<wrapper<T,Tag,Mixins...>>, T, Mixins...>::wrapper_impl;

    T& get() { return *reinterpret_cast<T*>(&m_buffer); }
    T const& get() const { return *reinterpret_cast<T const*>(&m_buffer); }

    template <typename...Args>
    void construct(Args&&...args) {
        new (&m_buffer) T(std::forward<Args>(args)...);
    }

    void destruct() {
        get().~T();
    }

    ~wrapper() {
        destruct();
    }

private:
    std::aligned_storage_t<sizeof(T), alignof(T)> m_buffer;;
};

现在,我可以使用以下mixin-template混合使用某种类型T的构造表单:

template <typename T>
struct constructor_from {
    template <typename Cast, typename U>
    struct mixin {
        explicit mixin(T const& value) {
            Cast::self(this).construct(U{value});
        }
    };
};

到目前为止,这很好,例如我可以这样包装一个int

using my_int = wrapper<int, struct Tag, constructor_from<int>::mixin>;
my_int instance{42};

请参阅godbolt上的完整代码。但是,我想禁用不需要的隐式转换,这就是我将constructor_from::mixin的构造函数标记为explicit的原因。但出乎意料的是,以下看似仍然在clang-5.0.0上编译没有错误(参见compiler-explorer):

using my_int = wrapper<int, struct Tag, constructor_from<int>::mixin>;
my_int instance{4.2};

的问题:

  • 这是clang-5.0.0中的错误吗?它似乎与gcc-7.3中的预期失败相吻合。
  • 如果它不是一个错误,我在做一些不合理的事(未定义,未指定,......),这解释了为什么它应该编译,尽管clang-5.0.0上有explicit构造函数?或者我可能误解了构造函数的继承是如何工作的?

编辑:

由于@someprogrammerdude有指针输出,我可能会误解explicit构造。但是,我仍然不明白为什么上面的代码应该编译,而this没有:

struct foo {
    explicit foo(int) {}
};

struct bar : foo {
    using foo::foo;
};

int main() {
    bar instance{4.2};
}

如果有人能够指出我在wrapper的情况下允许构建的c ++标准中的相应部分,那会很棒,但是在这种情况下可以防止它

0 个答案:

没有答案