是否有 C++20 中引入的 explicit(expr) 的 C++14 替代方案?

Is there a C++14 alternative to explicit(expr) introduced in C++20?

TL;DR:我正在寻找与以下 C++20 MWE 等效的 C++14:

template<int sz>
struct bits {
  int v; // note explicit(expr) below
  explicit(sz > 1) operator bool() const { return bool(v); }
};

int main() {
  bool c = bits<1>{1}; // Should work
  bool d = bits<3>{1}; // Should fail
}

上下文:

我们有一个 C++ class bits<sz> 表示长度为 sz 的位向量。向 bool 的转换过去对所有 sz 都是隐式的,但事实证明这很容易出错,因此我们将 operator bool() 更改为显式的。

然而,1-bit 位向量(在我们的上下文中)几乎完全等同于布尔值,因此当 sz == 1.[=46 时 operator bool() 是隐式的是可取的=]

这可以通过 C++20 中的 explicit(sz > 1) 实现,但我们的目标是 C++14。

我试图为 sz == 1 重载运算符,但似乎 explicit 限定符也适用于它:以下不起作用。

template<int sz>
struct bits {
  int v;
  explicit operator bool() const { return bool(v); }
};

template<> bits<1>::operator bool() const {
  return bool(v);
}

int main() {
  bool c = bits<1>{1}; // Fails: "No viable conversion"
}

因此出现问题:我如何在 C++14 中指定 operator bool() 应该仅对 sz > 1 显式?

我在下面为好奇的读者提供了一些背景知识。

背景:

这个问题是在 C++ 中的嵌入式领域特定语言的上下文中出现的。业务需求之一是 operator== returns 一个 bit<1>,而不是 bool。这与 GNU 的 libstdc++ 一起工作顺利,但我们 运行 在 macOS 上遇到了这个要求的麻烦,因为 libstdc++ 使用 std::equal 的版本在 std::array 上实现了 operator==一个谓词,并使用 operator() returns bool 和主体 a == b 的结构来实现该谓词(在我们的例子中 returns a bits<1>,导致转换错误)。

为了让好奇的读者具体了解,以下程序在 GNU 上编译良好,但在 macOS 上编译不好,因为 operator== on std::array 的实现方式:

#include <array>

struct S { explicit operator bool() const { return true; } };

struct T {};
S operator==(T, T) { return S(); }

int main() {
  std::array<T, 1> arr = { T() };
  return arr == arr;
}

那是因为在数组上 == 的深入实现中,GNU libstdc++ 有一个测试 if (!(*it1 == *it2)),它可以毫无问题地调用 S 上的 explicit operator bool(),其中在 macOS 上,库使用 if (!__pred(*it1, *it2))__pred 大致等同于 bool __pred(S a, S b) { return a == b; },它不进行类型检查。

是的。你can SFINAE 转换运算符:

#include <type_traits>

template<int sz>
struct bits {
  int v;
  explicit operator bool() const { return bool(v); }

  template <int S = sz>
  operator std::enable_if_t<S == 1, bool> () const { return bool(v);}
};

检查一下on godbolt

int main()
{
  bool c1 = bits<1>{1};                    // Ok
  bool c2 = bits<3>{1};                    // error cannot convert
  bool c3 = static_cast<bool>(bits<3>{1}); // OK
}