有没有办法检查 class 是否可转换为某些模板实例化?

Is there a way to check if a class is convertible to some template instantiation?

问题

这里是一些代码

struct Base
{
    int SomeMethod(const Base&);
    template <class T> int SomeMethod(const T&);
};

template <class Tag> struct Derived : Base
{
    using Base::SomeMethod;
    int SomeMethod(const Derived&);
    template <class OtherTag> std::enable_if_t<!std::is_same_v<Tag, OtherTag>> SomeMethod(Derived<OtherTag>) = delete;
};

struct tag {};
struct another_tag {};

struct ImplicitOpToAnotherTagInstantiation { operator Derived<another_tag>() const; };

有没有什么东西(Derived::SomeMethod 重载)我可以在 Derived 中写入而不触及 Base,这会导致下一个代码中的编译器错误?

Derived<tag>{}.SomeMethod(ImplicitOpToAnotherTagInstantiation {});

下一个代码应该是正确的

struct ImplicitOpToBase { operator Base() const; };

Derived<tag>{}.SomeMethod(Derived<tag>{});
Derived<tag>{}.SomeMethod(Base{});
Derived<tag>{}.SomeMethod(ImplicitOpToBase{});
// Derived<tag>{}.SomeMethod(Derived<another_tag>{}); compiler error!

我尝试了什么

template <class Tag> struct Derived : Base
{
    using Base::SomeMethod;
    int SomeMethod(const Derived&);
    template <class OtherTag> std::enable_if_t<!std::is_same_v<Tag, OtherTag>> SomeMethod(Derived<OtherTag>) = delete;
    template <class T, class OtherTag> std::enable_if_t<!std::is_same<Tag, OtherTag> && std::is_convertible<Tag, Derived<OtherTag>>> SomeMethod(T) = delete;
};

但它不起作用,因为如果没有像这样的显式实例化

,编译器无法推断 OtherTag
Derived<tag>{}.SomeMethod<ImplicitOpToAnotherTagInstantiation, another_tag>(); // error: deleted function!
template <class Tag> struct Derived : Base
{
    using Base::SomeMethod;
    int SomeMethod(const Derived&);
    template <class OtherTag> std::enable_if_t<!std::is_same_v<Tag, OtherTag>> SomeMethod(Derived<OtherTag>) = delete;
    template <class T> std::enable_if_t<!std::is_same<T, Derived> && std::is_convertible<T, Base>> SomeMethod(T) = delete;
};

但下一个代码会导致编译器错误,而此时不应该:

Derived<tag>{}.SomeMethod(ImplicitOpToBase{});

主要用途

我正在尝试为 std::string 创建某种强类型定义,其中 Basestd::stringDerived 是我的 TaggedStringSomeMethodstd::string::assign。我想用另一个标签的类型禁用 assign 方法,我在问题中解释了一些困难。为了 reader,我忽略了一个事实,即 TaggedString 可以隐式转换为 std::string_view

template <class Tag> struct TaggedString : std::string
{
    using std::string::assign;
    TaggedString& assign(const TaggedString& other) { std::string::assign(other); return *this; }
    template <class OtherTag> std::enable_if_t<!std::is_same_v<Tag, OtherTag>> assign(TaggedString<OtherTag>) = delete;
};

Here 是可以玩的。

没有。有无限组可能的转换,甚至是无限组到 Derived<SomeUnknownType> 的转换。您的编译器无法测试所有可能的类型 T 以查看 ImplicitOpToAnotherTagInstantiation 是否可以转换为 Derived<T>.

它可以测试一个有限集,如果你提供的话。但是你只有 tag,一个 exclude 的类型。无限类型集减去一种类型仍然是无限类型集。