继承和条件显式构造函数

问题描述 投票:0回答:1

用例是 std::expected 的子类,但以下内容重现了我感兴趣的行为。

#include <type_traits>

template<class T>
struct Foo{
  T value;

  constexpr explicit operator bool(){return true;}
  
  constexpr Foo() {}
  
  template<class U=T>
  constexpr explicit(!std::is_convertible_to<U,T>) 
  Foo(U&& v) : value(std::forward<U>(v)) {}
};

template <class T>
struct Bar : public Foo<T>  {
  using Foo<T>::Foo;
}

Bar
继承了
Foo
的所有构造函数,包括它们的“显式性”

但是。

在海湾合作委员会(12,13)中

//Foo<bool> foo() {return Foo<int();} //does not compile, as expected 
Bar<bool> bar(){ return Bar<int>();}
//compiles!

clang 不会表现出这种行为;即

Bar
的行为与
Foo
相同,并且尝试从返回
Bar<int>
的函数返回
Bar<bool>
无法编译。 神箭

将构造函数(错误,明确地)添加到 Bar 可以解决问题,但是对于 std::expected 中的所有许多复杂构造函数来说,这样做是......不可行的。

我的问题是:

了解标准的人能否阐明这是否是 gcc 中的错误,或者仅仅是一个漏洞?目前我不知道如何措辞错误报告。

最后,有人可以帮我找到解决方法(除了将大部分

<expected>
复制粘贴到我的小子类中)吗?我正在这个项目上使用 gcc-12,(std=c++23,linux,x64),clang 不支持预期的或 c++23 中的其他功能。


我尝试过的:

template<class T>
using Bar = Foo<T>;

可以工作,但无法自定义 Foo,这就是重点

template <class T>
struct Bar : public Foo<T> {
  Bar()=default;
  template<class...Args>
  Bar(Args&&...args){
    Foo<T>& self = *this;
    self = { std::forward<Args>(args)...};
  }
};

接近,但将要求从构造函数转移到operator=,plus需要T上的默认构造函数,并且不适用于具有多个参数的构造函数。 (std::expected 有一些带有标签:std::in_place_t、std::unexpect_t)

我也许可以解决上述问题,但它离透明包装越来越远。

这个问题涉及这个主题,但早于条件显式(c ++ 20)

这个问题 涉及 Intel 编译器,并在某些版本的标准中提到第 12.9 节,我读到它是说继承的构造函数的所有特征应该是相同的,这令人放心。

具有类似关键字的其他问题不处理条件显式加继承构造函数的交集


编辑:

我在 gcc bug 跟踪器上发现了 这些 bugs 与我的情况完全匹配。

这回答了我的第一个问题:是的,它是一个错误,它位于 gcc 错误跟踪器上,标准的相关位是 [namespace.udecl] p13

由 using 声明命名的构造函数被视为它们 查找派生类的构造函数时是派生类的构造函数 派生类 ([class.qual]) 或形成一组重载候选者 ([over.match.ctor]、[over.match.copy]、[over.match.list])。

仍然保持开放状态,希望能够想到一些解决方法。

c++ inheritance c++20 standards explicit-specialization
1个回答
0
投票

这似乎是一个错误。

template<class From, class To>
concept explicitly_convertible_to =  requires {
    static_cast<To>(std::declval<From>());
  };
template<class From, class To>
concept implicitly_convertible_to = std::is_convertible_v<From, To>;
template<class From, class To>
concept only_explicitly_converible_to =
  explicitly_convertible_to<From, To>
  && !implicitly_convertible_to<From, To>;

template<typename T>
struct Foo{
    T value;
    constexpr explicit operator bool(){return true;}
    constexpr Foo() {}

    template<implicitly_convertible_to<T> U>
    constexpr explicit(false) Foo(U&& v) :
      value(std::forward<U>(v))
    {
    }

    template<only_explicitly_converible_to<T> U>
    constexpr explicit(true) Foo(U&& v) :
      value(std::forward<U>(v))
    {
    }
};

template<typename T>
struct Bar : public Foo<T>{
    using Foo<T>::Foo;
};

当我按照上面的方式重写

Bar
时,
Foo
中继承的构造函数具有正确类型的
explicit
。如果我将
static_assert( this is not explicit )
添加到
Bar
构造函数,它会由
Bar<int>
Bar<bool>
隐式转换触发,但转换是隐式发生的。

template <class T>
struct Bar : public Foo<T> {
  Bar()=default;
  template<class...Args>
  Bar(Args&&...args):
    Foo<T>(std::forward<Args>(args)...)
  {}
};

我们可以从这里开始解决您的问题。如前所述,这会导致您丢失隐式/显式标志。我们重复我上面使用的技巧。

我们从

is_explicitly_constructible
开始,即
is_constructible
。现在我们尝试写
is_implicitly_constructible
:

template<class T, class...Args>
concept implicitly_constructable_from = requires(void(*f)(T), Args&&...args) {
  { f({std::forward<Args>(args)...}) };
};

这会在概念友好的上下文中调用复制列表初始化。

template<class T, class...Args>
concept explicitly_constructable_from = requires(Args&&...args) {
  { T(std::forward<Args>(args)...) };
};

template<class T, class...Args>
concept only_explicitly_constructable_from = 
  explicitly_constructable_from<T, Args...>
  && !implicitly_constructable_from<T, Args...>;

我们现在可以写我们的

Bar
:

template <class T>
struct Bar : public Foo<T> {
  Bar()=default;
  template<class...Args>
  requires only_explicitly_constructable_from< Foo<T>, Args... >
  explicit(true)
  Bar(Args&&...args):
    Foo<T>(std::forward<Args>(args)...)
  {}
  template<class...Args>
  requires implicitly_constructable_from< Foo<T>, Args... >
  explicit(false)
  Bar(Args&&...args):
    Foo<T>(std::forward<Args>(args)...)
  {}
};

我认为这可以满足你的大部分需求。

您将错过的最重要的事情是基于

{}
的(好吧,
Bar
)构造参数的构造。
    

© www.soinside.com 2019 - 2024. All rights reserved.