Q:

In function branch removal when a member is known at compile-time

Consider the following code:
// Class definition
class myclass
{
    public:
    constexpr myclass() noexcept: _value{0}, _option{true} {}
    constexpr myclass(int value) noexcept: _value{value}, _option{true} {}
    constexpr myclass(int value, bool option) noexcept: _value{value}, _option{option} {}
    constexpr int get_value() const noexcept {return _value;}
    constexpr int get_option() const noexcept {return _option;}
    private:
    int _value;
    bool _option;
};

// Some function that should be super-optimized
int f(myclass x, myclass y) 
{
    if (x.get_option() && y.get_option()) {
        return x.get_value() + y.get_value();
    } else {
        return x.get_value() * y.get_value();
    }
}

My question is the following: in such pattern, are compilers generally able to avoid the test when the option is known at compile-time, for example, when f(a, b) is called with a and b integers (in this situation the implicit one-parameter constructor is called, and the option is always true)? When I say "generally", I mean in complex real-world program, but where f is called on two int.

A:

The short answer is "depends". It depends on LOTS of things, including the complexity of the code, the compiler used, etc. 
In general, constant propagation (in other words, "converting something that was passed into a function as a constant, into the constant itself" is not a very difficult thing for compilers. Clang/LLVM does this very early on during compilation by having separate classes for "values we know are constants" and "values we don't know the value of" when generating the LLVM-IR ("intermediate representation", the layer of code built from source code, which doesn't represent actual machine code). Other compilers will also have similar constructs, both by using an IR, and by tracking constants separate from non-constant values.
So, assuming the compiler can "follow" the code (for example if f and the call to f are in different source files, it's unlikely to get optimised).
Of course, if you want to be SURE what your particular compiler does to YOUR particular code, you will have to check the code generated by the compiler.
// Class definition
class myclass
{
    public:
    constexpr myclass() noexcept: _value{0}, _option{true} {}
    constexpr myclass(int value) noexcept: _value{value}, _option{true} {}
    constexpr myclass(int value, bool option) noexcept: _value{value}, _option{option} {}
    constexpr int get_value() const noexcept {return _value;}
    constexpr int get_option() const noexcept {return _option;}
    private:
    int _value;
    bool _option;
};

// Some function that should be super-optimized
int f(myclass x, myclass y) 
{
    if (x.get_option() && y.get_option()) {
        return x.get_value() + y.get_value();
    } else {
        return x.get_value() * y.get_value();
    }
}

int main()
{
    myclass a;
    myclass b(1);
    myclass c(2, false);

    int x = f(a, b);
    int y = f(b, c);

    return x + y;
}

This will generate code that is identical to:
int main()
{
    return 3;
}

However, if we alter the code to this:
#include "myclass.h"

extern int f(myclass x, myclass y);

int main()
{
    myclass a;
    myclass b(1);
    myclass c(2, false);

    int x = f(a, b);
    int y = f(b, c);

    return x + y;
}

and declare f in a separate file (with -O2 optimisation), the resulting code is
define i32 @_Z1f7myclassS_(i64 %x.coerce, i64 %y.coerce) #0 {
entry:
  %x.sroa.0.0.extract.trunc = trunc i64 %x.coerce to i32
  %y.sroa.0.0.extract.trunc = trunc i64 %y.coerce to i32
  %conv.i = and i64 %x.coerce, 1095216660480
  %tobool = icmp eq i64 %conv.i, 0
  %conv.i12 = and i64 %y.coerce, 1095216660480
  %tobool2 = icmp eq i64 %conv.i12, 0
  %or.cond = or i1 %tobool, %tobool2
  %add = add nsw i32 %y.sroa.0.0.extract.trunc, %x.sroa.0.0.extract.trunc
  %mul = mul nsw i32 %y.sroa.0.0.extract.trunc, %x.sroa.0.0.extract.trunc
  %retval.0 = select i1 %or.cond, i32 %mul, i32 %add
  ret i32 %retval.0
}

and main:
define i32 @main() #0 {
entry:
  %call = tail call i32 @_Z1f7myclassS_(i64 4294967296, i64 4294967297)
  %call4 = tail call i32 @_Z1f7myclassS_(i64 4294967297, i64 2)
  %add = add nsw i32 %call4, %call
  ret i32 %add
}

As you can see, the argument to f is converted to two 64-bit integers, and the value of option is stored in the upper half of the 64-bit value. The function f is then splitting the 64-bit value into two parts, and deciding whether to return the result of multiplication or addition based on the value.

