1

The following code does not compile. Is it even possible to pass an instance of Configuration<t, u> as a template parameter? (to profit from optimization of constant expressions)

API:

template <int t, bool u>
struct Configuration {
  static constexpr int commonValue = t;
  static constexpr bool debug = u;
};

template <
  template<int t, bool u> Configuration<t, u> &conf,
  int x
>
class A {
public:
  int doSomething() {
    if (conf.debug) { // Optimize away if false
      // ...
    }
    return x * conf.commonValue; // Optimize to e.g. 6 with `conf.commonValue` =
                                 // 3, `x` = 2
  }
};

The user of the API should be able to do:

int main() {
    static constexpr Configuration<3, false> conf;
    A<conf, 2> a;
    A<conf, 5> b;
    A<conf, 8> c;
    std::cout << a.doSomething() << std::endl; // 6 (evaluated at compile time!)
    std::cout << b.doSomething() << std::endl; // 15
    std::cout << c.doSomething() << std::endl; // 24
}
feklee
  • 7,555
  • 9
  • 54
  • 72
  • Since you only have `static` attribute in `Configuration`, use a template type parameter. – Holt Jun 13 '18 at 14:51

1 Answers1

3

Since the attributes of Configuration are static, you should use a template type parameter1:

template <class ConfT, int x>
class A {
public:
  int doSomething() {
    if (ConfT::debug) { // Optimize away if false

    }
    return x * ConfT::commonValue;
  }
};

And then:

// Alias (not required):
using conf = Configuration<3, false>;

A<conf, 2> a;
A<conf, 3> b;

If you want non-static members, I don't think this is doable pre-C++17 (without passing template parameters of Configuration to A), but in C++17 you could do1:

template <auto const& conf, int x>
class A {
public:
  int doSomething() {
    if (conf.debug) { // Optimize away if false
    }
    return x * conf.commonValue;
  }
};

But note that you can only pass references to variables with static storage duration.

1 In the first case, you can restrict the type to Configuration<...> by specializing A, in the second case, you can restrict the type of conf using an extra template parameter with std::enable_if.


You should also make the attributes constexpr in Configuration, not simply const:

template <int t, bool u>
struct Configuration {
  static constexpr int commonValue = t;
  static constexpr bool debug = u;
};
feklee
  • 7,555
  • 9
  • 54
  • 72
Holt
  • 36,600
  • 7
  • 92
  • 139
  • The 1st example does not compile with `g++ -std=C++17`. At `A a;` it complains: `the value of ‘conf’ is not usable in a constant expression` With the 2nd example, at `template `. the compiler complains: `‘auto’ parameter not permitted in this context`. Thanks for the suggestion about using `constexpr`! Although: Looking at assembly code I found that specifying `static const` is generally sufficient for `g++` to apply compile time optimizations. Nevertheless, I edited my question, replacing `const` with `constexpr` (as this distracts from the actual issue). – feklee Jun 13 '18 at 15:28
  • @feklee As I mentioned, for the second example, you need `conf` to have static storage duration, so typically not a local variable (unless you add a `static` modifier), see this example: https://godbolt.org/g/6Xd9hs Note that you need a quite recent version of gcc apparently (at least 7.1 according to godbolt). – Holt Jun 13 '18 at 15:30
  • @Holt Thanks, indeed the compiler I tested it with seems to have been too old. *What about the first example?* I cannot get it to compile at all. It would be nice to have sth. that works with C++11, but I have to accept it if that's not possible. – feklee Jun 13 '18 at 15:42
  • Well, the first example does work, if I replace `constexpr Configuration<3, false> conf;` with `typedef Configuration<3, false> conf;`. *Is that what you had in mind?* – feklee Jun 13 '18 at 16:05
  • @feklee Yes, exactly, I've added example of usage in the answer (`typedef` and `using` are the same thing here). – Holt Jun 13 '18 at 16:45