在模板SFINAE约束中使用间接级别会导致硬错误

时间:2019-05-04 18:36:34

标签: c++ templates language-lawyer c++17 sfinae

在以下代码(https://wandbox.org/permlink/rA7lnXM6eQR4JhSM)中

#include <type_traits>

template <typename T>
struct Identity : public T {};

class Something {
public:
  Something() = default;
  Something(const Something&) = delete;
  Something(Something&&) = default;
  Something& operator=(const Something&) = default;
  Something& operator=(Something&&) = default;

  template <
      typename T,
      typename U = std::decay_t<T>,
      std::enable_if_t<Identity<
        std::is_constructible<U, T&&>>::value>* = nullptr>
  explicit Something(T&&) {};
};

int main() {
    static_cast<void>(std::is_constructible<Something, const Something&>{});
}

我收到以下错误

error: base class has incomplete type
struct Identity : public T {};
                  ~~~~~~~^

当我在此约束(https://wandbox.org/permlink/MFJCHUzeKnS4yR0d)中使用Identity删除间接访问时,错误消失了

  template <
      typename T,
      typename U = std::decay_t<T>,
      std::enable_if_t<
        std::is_constructible<U, T&&>::value>* = nullptr>
  explicit Something(T&&) {};

根据我的理解,这里的问题是我们试图实例化std::is_constructible,然后实例化Something的构造函数,然后实例化std::is_constructible,依此类推。

但是,当我尝试不使用Identity进行编译时,为什么错误消失了?为什么在我使用Identity时会出错?

1 个答案:

答案 0 :(得分:1)

您已经确定存在一个“递归实例化”。没有真正的递归;在“有效”的情况下发生的事情很简单,当考虑构造函数模板以找出从const Something&构造是否成功时,std::is_constructible<Something, const Something&>没有成员value 。 (与普遍看法相反,类C不需要用C::来命名成员,但是必须已经声明了命名成员,并且“已经”的确切含义是{{3 }}。

该错误出现在构造函数模板的立即上下文中,因此该模板被忽略。 (在这种情况下,这没有什么区别:(删除的)副本构造函数是更好的匹配,因为它不是模板。)这可以确定丢失的valuefalse,至少一致,这是第一次拒绝构造函数模板。对于std::is_constructible本身的非常专业化,这可能违反了a bit murky;考虑到如果我们在控制自身的enable_if中取消条件,则会随之而来。

您的Identity遇到相同的错误(或更普遍的相关专业化问题尚未完成)。作为辅助实例的一部分,该错误是无法恢复的。