Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

if / else at compile time in C++?

Tags:

Consider the following code :

#include <iostream> #include <type_traits>  template<typename T> class MyClass {     public:         MyClass() : myVar{0} {;}         void testIf() {             if (isconst) {                 myVar;             } else {                 myVar = 3;             }         }         void testTernary() {             (isconst) ? (myVar) : (myVar = 3);         }      protected:         static const bool isconst = std::is_const<T>::value;         T myVar; };  int main() {     MyClass<double> x;     MyClass<const double> y;     x.testIf();     x.testTernary();     y.testIf(); // <- ERROR     y.testTernary(); // <- ERROR     return 0; } 

For x (non-const) there is no problem. But y (const data type) cause an error even if the condition in if/else is known at compile-time.

Is there any possibility to not compile the false condition at compile-time ?

like image 216
Vincent Avatar asked Aug 28 '12 13:08

Vincent


2 Answers

C++17 if constexpr

Oh yes, it has arrived:

main.cpp

#include <cassert> #include <type_traits>  template<typename T> class MyClass {     public:         MyClass() : myVar{0} {}         void modifyIfNotConst() {             if constexpr(!isconst) {                 myVar = 1;             }         }         T myVar;      protected:         static constexpr bool isconst = std::is_const<T>::value; };  int main() {     MyClass<double> x;     MyClass<const double> y;     x.modifyIfNotConst();     y.modifyIfNotConst();     assert(x.myVar == 1);     assert(y.myVar == 0);     return 0; } 

GitHub upstream.

Compile and run:

g++-8 -std=c++17 -Wall -Wextra -pedantic -o main.out main.cpp ./main.out 

See also: Difference between "if constexpr()" Vs "if()"

This will be really cool together with C++20 "string literal template arguments": Passing a string literal as a parameter to a C++ template class

Tested in Ubuntu 16.04, GCC 8.1.0.


The simplest fix is partial template specialization:

template<typename T> class MyClassBase {     public:         MyClassBase() : myVar{0} {;}      protected:         T myVar; };  template<typename T> class MyClass: MyClassBase<T> {     public:         void testIf() { myVar = 3; } };  template<typename T> class MyClass<const T>: MyClassBase<const T> {     public:         void testIf() { myVar; } }; 

Another option is delegation:

template<typename T> class MyClass {     public:         MyClass() : myVar{0} {;}         void testIf() { testIf_impl(std::integral_constant<bool, isconst>()); }      protected:         static const bool isconst = std::is_const<T>::value;         T myVar;      private:         void testIf_impl(std::true_type) { myvar; }         void testIf_impl(std::false_type) { myVar = 3; } }; 

SFINAE is another option, but is generally not preferred for this case:

template<typename T> class MyClass {     public:         MyClass() : myVar{0} {;}         template         <typename U = void>         typename std::enable_if<std::is_const<T>::value, U>::type testIf() { myvar; }         template         <typename U = void>         typename std::enable_if<!std::is_const<T>::value, U>::type testIf() { myvar = 3; }      protected:         static const bool isconst = std::is_const<T>::value;         T myVar; }; 
like image 41
ecatmur Avatar answered Oct 14 '22 08:10

ecatmur