简体   繁体   中英

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

TL;DR : I am looking for a C++14 equivalent of the following C++20 MWE:

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
}

Context:

We have a C++ class bits<sz> representing bitvectors of length sz . Conversion to bool used to be implicit for all sz , but this proved to be error-prone, so we changed operator bool() to be explicit.

However, 1-bit bitvectors are (in our context) almost-completely equivalent to Booleans, so it would be desirable for operator bool() to be implicit when sz == 1 .

This can be achieved with explicit(sz > 1) in C++20 , but we are targeting C++14.

I tried to overload the operator for sz == 1 , but it seems that the explicit qualifier applies to it as well: the following does not work.

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"
}

Hence the question: How can I specify in C++14 that operator bool() should be explicit only for sz > 1 ?

I'm including some background below for curious readers.

Background :

This problem came up in the context of an embedded domain-specific language in C++. One of the business requirements is that operator== returns a bit<1> , not a bool . This is working smoothly with GNU's libstdc++, but we're running into trouble with that requirement on macOS because libstdc++ there implements operator== on std::array using the version of std::equal that takes a predicate, and implements that predicate using a struct whose operator() returns bool with body a == b (which in our case returns a bits<1> , causing a conversion error).

To make it concrete for curious readers, the following program compiles fine on GNU, but not on macOS, because of the way operator== on std::array is implemented:

#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;
}

That's because deep down in the implementation of == on arrays GNU libstdc++ has a test if (!(*it1 == *it2)) , which invokes the explicit operator bool() on S without trouble, where as on macOS the library uses if (,__pred(*it1, *it2)) with __pred roughly equivalent to bool __pred(S a, S b) { return a == b; } bool __pred(S a, S b) { return a == b; } , which doesn't typecheck.

Yes. You can SFINAE the conversion operator:

#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);}
};

Check it 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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM