Oct 2, 2017

[c++][cppcon 2017] constexpr all the things

constexpr all the things

Reference:
cppcon2017 constexpr all the things
constexpr specifier
Constant expressions
std::variant

digress:
User-defined literals (From c++11 faq)

A literal operator can request to get its (preceding) literal passed ``cooked''
(with the value it would have had if the new suffix hadn't been defined) or ``uncooked'' (as a string).
constexpr complex<double> operator "" i(long double d) // imaginary literal
 {
  return {0,d}; // complex is a literal type
 }
 
std::string operator""s (const char* p, size_t n) // std::string literal
 {
  return string(p,n); // requires free store allocation
 }

To get an ``uncooked'' string, simply request a single const char* argument:
Bignum operator"" x(const char* p)
{
 return Bignum(p);
}

void f(Bignum);
f(1234567890123456789012345678901234567890x);


There are four kinds of literals that can be suffixed to make a user-defined literal
  • integer literal
    accepted by a literal operator taking a single unsigned long long or const char* argument.
  • floating point literal
    accepted by a literal operator taking a single long double or const char* argument.
  • string literal
    accepted by a literal operator taking a pair of (const char*, size_t) arguments.
  • character literal
    accepted by a literal operator taking a single char argument.
Suffixes will tend to be short (e.g. s for string, i for imaginary, m for meter, and x for extended),
so different uses could easily clash. Use namespaces to prevent clashes:
namespace Numerics { 
  // ...
  class Bignum { /* ... */ }; 
  namespace literals { 
   operator"" X(char const*); 
  } 
 } 

 using namespace Numerics::literals; 


Benefit of constexpr
  • Runtime efficiency
  • Clearer code, fewer magic numbers
  • Less cross-platform pain

Requirements for compile time types
  • constexpr constructor
  • std::is_trivially_destructible
constexpr allocator
template <class T, size_t Size>
struct ConstexprAllocator {
    typedef T value_type;
    consstexpr ConstexprAllocator(/*ctor args*/);
    template <class U>
    constexpr ConstexprAllocator(const ConstexprAllocator<U>& other);
    constexpr T* allocate(std::size_t n);
    constexpr void deallocate(T* p, std::size_t n);
    std::array<std::pair<bool, value_type>, Size> data; // bool for free flag
};

Currently any type with a non-trivial destructor cannot be used in constexpr context.
Reason:
    Run-time adjusting this pointer.

Solution to the constexpr destructor problem
struct Container {
    ~Container() {
    // this proposal allows for an empty destructor to be allowed
        if constexpr(something) {
        // do something
        }
    }
};

// OR
struct Container {
    ~Container() {
    // but why not treat it like any other constexpr code?
    // allow it as long as only constexpr allowed actions
    // happen at compile time?
        if (extra_data) {
            delete [] extra_data;
        }
    }
};

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.