首页 > 解决方案 > 在模板 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 {};
                  ~~~~~~~^

Identity当我删除约束中的间接时,错误消失了( https://wandbox.org/permlink/MFJCHUzeKnS4yR0d

  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

标签: c++templateslanguage-lawyerc++17sfinae

解决方案


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

该错误位于构造函数模板的直接上下文中,因此模板被静默忽略。(这在这种情况下没有区别:(已删除的)复制构造函数是更好的匹配,因为它不是模板。)这确定了value缺少的是false,这至少与第一次拒绝构造函数模板一致. 这可能违反[meta.rqmts]/5本身的专业化std::is_constructibleenable_if考虑一下,如果我们否定控制自身的条件,就会产生欢闹。

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


推荐阅读