Reputation: 60341
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 ?
Upvotes: 26
Views: 32661
Reputation: 382472
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;
}
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.
Upvotes: 29
Reputation: 157314
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;
};
Upvotes: 15
Reputation: 18905
Try this:
template<typename T>
class MyClass
{
T myVar;
public:
MyClass() : myVar(0) {}
void testIf()
{
assign(myVar, 3);
}
private:
template<typename V>
void assign(V& destination, int value)
{
destination = value;
}
template<typename V>
void assign(const V& destination, int value)
{
}
};
Upvotes: 0
Reputation: 92211
You can specialize the class for const types
template<typename T>
class MyClass
{
// Whatever you need to do
};
template<typename T>
class MyClass<const T>
{
// Whatever you need to do for const types
};
Upvotes: 5
Reputation: 21900
The class template gets compiled for the given type. Even if the control flow doesn't get to the assignment, that assignment is compiled as well. Since the member is const, the compilation will fail.
You could use some form of SFINAE to skip that assignment, but it's not going to work as it is now.
This works(I've removed the testTernary
member function for simplicity):
#include <iostream>
#include <type_traits>
template<typename T> class MyClass
{
public:
MyClass() : myVar{0} {;}
template<class U = T>
typename std::enable_if<std::is_const<U>::value>::type testIf() {
myVar;
}
template<class U = T>
typename std::enable_if<!std::is_const<U>::value>::type testIf() {
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();
y.testIf();
return 0;
}
Upvotes: 3
Reputation: 6745
If the else branch weren't compiled, then your function would have a completely different meaning. You can't just not compile part of your code. If you don't want it to execute, don't write it. It's not like the function is compiled separately for each time it is called.
The whole point of a type system is to avoid accidentally trying to do things like assigning to const
variables. You would have to write an entirely new (or overloaded) function that doesn't assign to that variable.
Upvotes: 0