[PATCH] [libcxx] Support UDTs convertible to arithmetic types in <cmath>

Eric Fiselier eric at efcs.ca
Mon Dec 8 20:17:59 PST 2014


The problem I see with this patch is that the expression we use to check for convertibility in `__numeric_type` is often very different from the expression that actually performs the conversion. The expression that checks for convertibility is:
```
    __numeric_test(declval<T>());
```
and the expression that actually converts T is:
```
static_cast<T>(t);
```


First, we are checking for implicit conversions and preforming explicit ones. This causes a hard compile error in the following code:
```
struct ImplicitExplicit
{
    operator float() { return 0.0; }
private:
  explicit operator double() { return 0.0; }
};

// Doesn't fire
static_assert(is_same<
    __numeric_type<ImplicitExplicit>::type,float
 >::value, "");

ImplicitExplicit value;
// Fails to compile.
double d = static_cast<double>(value);

// Example in cmath:
std::isless(value, (float)0.0); // Compiles
std::isless(value, (double)0.0); // Doesn't compile.

```

Second, we are checking for conversions with rvalue references and preforming conversions with lvalue references. This causes a compile error in the following code:
```
struct RValueConvertible
{
    operator double() && { return 0.0; }
};

// Doesn't fire.
static_assert(__numeric_type<RValueConvertible>::value, "");

// Example of error in cmath
RValueConvertible value;
std::isnan(value); // Doesn't compile.
```
Third we copy/move the actual types into the cmath functions before preforming the conversion, but we probably shouldn't do this. The following code will not compile for this reason
```
struct NonCopyable
{
   NonCopyable() {}
   operator int() { return 1; }
private:
  NonCopyable(NonCopyable const &);
};

static_assert(__numeric_type<NonCopyable>::value, ""); // Doesn't fire

NonCopyable value;
std::isnan(value);
```

I'm not sure these problems should hold this patch up, but I would like to see some discussion on it before moving forward.
I think your implementation of std::pow solves each of these problems with a substantial increase in code complexity, but I think with some work a cleaner solution could be reached.
However it seems that every cmath function will have to perfect forward in order to solve all of the problems :(

================
Comment at: include/cmath:944-949
@@ -962,1 +943,8 @@
+pow(_A1&& __lcpp_x, _A2&& __lcpp_y)
+    _NOEXCEPT_
+    (
+        _NOEXCEPT_(__promote<_A1>::__does_not_throw) &&
+        _NOEXCEPT_(__promote<_A2>::__does_not_throw)
+    )
+#endif
 {
----------------
Why does `pow` have the noexcept clause on it anyway?

================
Comment at: include/type_traits:1204-1208
@@ -1201,7 +1203,7 @@
 
 template <>
 struct __numeric_type<void>
 {
-   static const bool value = true;
+    static const bool value = true;
 };
 
----------------
My personal preference would be to use a dummy type instead of  void for this.

http://reviews.llvm.org/D5942






More information about the cfe-commits mailing list