17

This used to work some weeks ago:

template <typename T, T t>
T            tfunc()
{
    return t + 10;
}

template <typename T>
constexpr T       func(T t)
{
    return tfunc<T, t>();
}

int main()
{
    std::cout << func(10) << std::endl;
    return 0;
}

But now g++ -std=c++0x says:

main.cpp: In function ‘constexpr T func(T) [with T = int]’:
main.cpp:29:25:   instantiated from here
main.cpp:24:24: error: no matching function for call to ‘tfunc()’
main.cpp:24:24: note: candidate is:
main.cpp:16:14: note: template<class T, T t> T tfunc()
main.cpp:25:1: warning: control reaches end of non-void function [-Wreturn-type]

clang++ -std=c++11 says that template's parameters of tfunc<T, t>() are ignored because invalid.

Is that a bug, or a fix ?

PS:

g++ --version => g++ (GCC) 4.6.2 20120120 (prerelease)

clang++ --version => clang version 3.0 (tags/RELEASE_30/final) (3.0.1)

Gravemind
  • 191
  • 1
  • 1
  • 5

4 Answers4

10

The parameter t is not a constant expression. Hence the error. It should be also noted that it cannot be a constant expression.

You can pass the constant expression as argument, but inside the function, the object (the parameter) which holds the value, is not a constant expression.

Since t is not a constant expression, it cannot be used as template argument:

return tfunc<T, t>(); //the second argument must be a constant expression

Maybe, you want something like this:

template <typename T, T t>
T  tfunc()
{
    return t + 10;
}

template <typename T, T t>  //<---- t became template argument!
constexpr T  func()
{
    return tfunc<T, t>();
}

#define FUNC(a)  func<decltype(a),a>()

int main()
{
    std::cout << FUNC(10) << std::endl;
}

Now it should work : online demo

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • I am still confused by the sentence : *an argument to a function cannot be a const expression* – Mr.Anubis Jan 28 '12 at 13:40
  • @Mr.Anubis: That is why I explained that part in the next para. Did you read it? – Nawaz Jan 28 '12 at 13:40
  • you mean `..func(T t)` inside `func` , `t` in not constexp , right? – Mr.Anubis Jan 28 '12 at 13:45
  • That's not true. `10` is actually a constant expression. – Johannes Schaub - litb Jan 28 '12 at 13:45
  • @JohannesSchaub-litb: I know that is a const expression. And I also know that you didn't read the second para in my post. – Nawaz Jan 28 '12 at 13:46
  • 1
    lol "And I also know that you didn't read the second para in my post." – Mr.Anubis Jan 28 '12 at 13:48
  • 1
    @Nawaz I don't need to read a correct second part if I want to complain about the wrong first part, do I? – Johannes Schaub - litb Jan 28 '12 at 13:49
  • @JohannesSchaub-litb: I think, sometimes some sentences can be interpreted in many different ways, that is why authors write few more sentences to explain that part which seems confusing (or even wrong). I know that some other author (or even the same author) can re-write the whole thing, making things clear from the first line. – Nawaz Jan 28 '12 at 13:52
  • @Mr.Anubis: No. There is a difference between *readonly* expression and *constant* expression. If you write `f(const T t)`, then `t` is *readonly* expression. – Nawaz Jan 28 '12 at 13:59
  • I know my code has worked some weeks ago, I just simplify it for the example. A `constexpr` function is evaluated at compile-time, so I don't know why now (seems to be with a new version of gcc), it can't resolve `tfunc` – Gravemind Jan 28 '12 at 14:00
  • 2
    @Gravemind because a constexpr function is a *function*. The function behind this still is a normal function. That this fails has nothing to do with `constexpr`, but just with the normal rule that function parameters cannot be used as template arguments. – Johannes Schaub - litb Jan 28 '12 at 14:03
  • Ok, I missed the fact that `constexpr` functions are just simple functions that can be evaluated at compile-time. But, why g++ force the evaluation as in a run-time context even if g++ knows that `func(10)` can be evaluated at compile-time (it can also explain why before it works perfectly). – Gravemind Jan 28 '12 at 14:13
  • The parameter may be a constant-expression (indirectly). Consider `template constexpr T func(T t) { return t+t; }` Here, the compiler knows how to implement `+` both at compile-time and at run-time, and hence this code is valid and is suitable for compile-time execution. So, parameters to a `constexpr` function are a bit schizophrenic - sometimes they are constant-expressions, sometimes they are not. – Aaron McDaid Jan 28 '12 at 14:14
  • @Gravemind, if you intend that the parameter to func will *only* be used in a compile-time context, then func should take a template int parameter. By using `constexpr` and simple parameters, you are requesting that the function be compiled/compilable in both contexts, run-time and compile-time. (That's my intuition at the moment anyway.) – Aaron McDaid Jan 28 '12 at 14:17
  • @AaronMcDaid: In other words, (according to you), this should work : http://ideone.com/xILLF ... but it doesn't. – Nawaz Jan 28 '12 at 14:18
  • @AaronMcDaid : the puprose of my code is to use the type inference of `func(10)` (here: int) to call the template function `tfunc` that take a generic type/value as template parameter. – Gravemind Jan 28 '12 at 14:22
  • @Nawaz : I've already used `decltype` in a define, but it's awful and I want to stay in c++11 meta-programming only. – Gravemind Jan 28 '12 at 14:24
  • 1
    @Nawaz, that example works on g++-4.6. And I think that's the correct behaviour, and that ideone is out-of-date. But I might be wrong. – Aaron McDaid Jan 28 '12 at 14:30
  • So, what do I do ? do I downgrade to g++-4.6 or hope that was a regression and wait for a fix ? – Gravemind Jan 28 '12 at 14:48
  • @Gravemind, I don't think you need to downgrade (I'm not sure I understand why you suggested you might downgrade!). Ultimately, not all `constexpr` are constant-expressions. There are two solutions to the original problem - either upgrade the func parameter to a full constant-expression (i.e Nawaz's solution in this answer), *or* to downgrade the parameter that is accepted by the `tfunc::tfunc` constructor from a template-paremeter to a mere `constexpr`. – Aaron McDaid Jan 28 '12 at 15:51
  • @AaronMcDaid, I want to downgrade because this works on g++-4.6. The only point to do that, is to have a simple syntax for calling a template function (`tfunc`) with a generic static value template parameter (`T t`). Yes, I can do `tfunc()` or `tfunc< a_type_long_as_hell, my_simple_value >()`, but it's going to be unreadable very quick inside the rest of my code (and I don't want to obfuscate my code with macros). So, I'm still looking for a way to have at the beginning `indirect_func(simple_value)` (with 1 parameter) and at the end a call of `template void tfunc()`. – Gravemind Jan 28 '12 at 16:19
  • @Gravemind, do you have any helpful bounds on `T` and `t`? For example, if they were guaranteed to be `int` between `0` and `10`, then you could easily write `func` with a `switch` statement. In order to make `func` into a `constexpr` you would need to replace the switch with something like `return t==0 ? tfunc() : t==1 ? tfunc() ? t==2 ? tfunc() : ` ... and so on. – Aaron McDaid Jan 28 '12 at 18:09
  • @Gravemind, I've added another answer here to directly tackle your goal - although it has other problems. – Aaron McDaid Jan 28 '12 at 21:01
  • you said in previous comments : "There is a difference between readonly expression and constant expression. If you write f(const T t), then t is readonly expression" . Can you please link to some articles which explains about all this in deep? , Thanks – Mr.Anubis Jan 29 '12 at 10:25
  • @Mr.Anubis: You can read [this topic](http://stackoverflow.com/questions/5248571/is-there-const-in-c). If still not satisfied, then create a new topic, and quote my statement there, and ask for detail explanation. I'm sure many good answers you will get. – Nawaz Jan 29 '12 at 10:49
  • @AaronMcDaid, In fact, if you want to know what I am doing with all that, this is for a simple implementation of [impossibly fast delegates](http://www.codeproject.com/Articles/11015/The-Impossibly-Fast-C-Delegates). So, `func` is used like this `bind(&A::method)` and (in g++-4.6) it calls `_bindMethod()` (for example), then I use it to create my fast delegate. But, since g++-4.6.2, according to the [c++11 constexpr standart](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf), this doesn't work anymore. – Gravemind Jan 29 '12 at 13:58
  • @Gravemind, that blog piece is very interesting. But I must admit I don't see the connection directly. Are you essentially saying that you are unable to change the signatures of `func` or `tfunc`, but you still wish them to be able to call each other? i.e. `func` must have the same signature because it's going to be used in that delegate? – Aaron McDaid Jan 29 '12 at 15:21
  • @Gravemind, .... perhaps you want to be able to take the address of the templated function, much like was done in the blog post you linked to? Like `&tfunc` ? – Aaron McDaid Jan 29 '12 at 15:30
  • @AaronMcDaid, exactly, and use `func(&A::f)` to simplify and generalize the syntax by using type inference of template functions. But now, I know it uses a bug of g++-4.6: in a static context (`func(10)` or `func(&A::f)`), my `constexpr func` is consider totally static (statements, return, and arguments). `func` was a meta-programming function but strongly typed (really nice concept by the way). So I haven't completely understood c++11 constexpr. So, now I use a macro as ugly as it is. – Gravemind Jan 30 '12 at 08:47
  • 1
    @Aaron parameters of constexpr functions are never constant expressions. Any time you call a constexpr function within a constant expression, function invocation substitution is done. Any parameter names used in the `return` statement are then replaced by the argument expression. So no parameters are used, but the arguments are used directly. – Johannes Schaub - litb Jan 30 '12 at 21:14
2

I get the feeling that constexpr must also be valid in a 'runtime' context, not just at compile-time. Marking a function as constexpr encourages the compiler to try to evaluate it at compile-time, but the function must still have a valid run-time implementation.

In practice, this means that the compiler doesn't know how to implement this function at runtime:

template <typename T>
constexpr T       func(T t)
{
    return tfunc<T, t>();
}

A workaround is to change the constructor such that it takes its t parameter as a normal parameter, not as a template parameter, and mark the constructor as constexpr:

template <typename T>
constexpr T       tfunc(T t)
{
    return t + 10;
}
template <typename T>
constexpr T       func(T t)
{
    return tfunc<T>(t);
}

There are three levels of 'constant-expression-ness':

  1. template int parameter, or (non-VLA) array size // Something that must be a constant-expression
  2. constexpr // Something that may be a constant-expression
  3. non-constant-expression

You can't really convert items that are low in that list into something that is high in that list, but obviously the other route it possible.

For example, a call to this function

constexpr int foo(int x) { return x+1; }

isn't necessarily a constant-expression.

// g++-4.6 used in these few lines. ideone doesn't like this code. I don't know why
int array[foo(3)]; // this is OK
int c = getchar();
int array[foo(c)]; // this will not compile (without VLAs)

So the return value from a constexpr function is a constant expression only if all the parameters, and the implementation of the function, can be completed at executed at compile-time.

Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
  • `constexpr int i = something_that_MUST_be_a_constexpr;` – Xeo Jan 28 '12 at 14:12
  • @Xeo, it must *at least* be a `constexpr`. Whereas `template struct X; X x;` requires that `i` be even more than a `constexpr`. – Aaron McDaid Jan 28 '12 at 14:19
  • Maybe I shouldn't have used the second `constexpr` as a short-hand for `constant expression`... :) I just wanted to say that for `constexpr`, sometimes something *must* be a constant expression - when a variable is declared as such. (As an answer to your "`constexpr` // Something that *may* be a constant-expression".) – Xeo Jan 28 '12 at 14:30
  • This example shows exactly why I don't understand why my code doesn't work: `foo(3)` and `foo(c)` are 2 totally different interpretations by g++. `foo(3)` will be evaluated at compile-time, and `foo(c)` will generate the code to do it at run-time. So, why `foo(3)` is evaluated as a run-time function ? – Gravemind Jan 28 '12 at 14:34
  • @Xeo, I agree that "*may*" is a bad choice of word. I'll think further about how to edit that, and you can feel free to edit it yourself. There is a certain 'split-personality' around `constexpr`. A `constexpr` only graduates to be a full 'constant-expression' if certain conditions hold. I'm sure you understand that already, the challenge is to summarize this for the layman. [ And I'm no expert, hence I'm furiously testing all this on various compilers - where each compiler is behaving a little differently :-) ] – Aaron McDaid Jan 28 '12 at 14:36
2

Recap the question: You have two functions which take a parameter of type T. One takes its parameter as a template parameter, and the other as a 'normal' parameter. I'm going to call the two functions funcT and funcN instead of tfunc and func. You wish to be able to call funcT from funcN. Marking the latter as a constexpr doesn't help.

Any function marked as constexpr must be compilable as if the constexpr wasn't there. constexpr functions are a little schizophrenic. They only graduate to full constant-expressions in certain circumstances.

It would not be possible to implement funcN to run at runtime in a simple way, as it would need to be able to work for all possible values of t. This would require the compiler to instantiate many instances of tfunc, one for each value of t. But you can work around this if you're willing to live with a small subset of T. There is a template-recursion limit of 1024 in g++, so you can easily handle 1024 values of T with this code:

#include<iostream>
#include<functional>
#include<array>
using namespace std;

template <typename T, T t>
constexpr T funcT() {
        return t + 10;
}

template<typename T, T u>
constexpr T worker (T t) {
        return t==0 ? funcT<T,u>() : worker<T, u+1>(t-1);

}
template<>
constexpr int worker<int,1000> (int ) {
            return -1;
}


template <typename T>
constexpr T       funcN(T t)
{
        return t<1000 ? worker<T,0>(t) : -1;
}

int main()
{
    std::cout << funcN(10) << std::endl;
    array<int, funcN(10)> a; // to verify that funcN(10) returns a constant-expression
    return 0;
}

It uses a function worker which will recursively convert the 'normal' parameter t into a template parameter u, which it then uses to instantiate and execute tfunc<T,u>.

The crucial line is return funcT<T,u>() : worker<T, u+1>(t-1);

This has limitations. If you want to use long, or other integral types, you'll have to add another specialization. Obviously, this code only works for t between 0 and 1000 - the exact upper limit is probably compiler-dependent. Another option might be to use a binary search of sorts, with a different worker function for each power of 2:

template<typename T, T u>
constexpr T worker4096 (T t) {
        return t>=4096 ? worker2048<T, u+4096>(t-4096) : worker2048<T, u>(t);

}

I think this will work around the template-recursion-limit, but it will still require a very large number of instantiations and would make compilation very slow, if it works at all.

Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
  • you can verify a `constexpr` function by declaring an `constexpr auto` or the specific `return type` in the main(since c++17). Basically `constexpr auto var = funcN(10)`. If it works, it's constexpr. – M.Mac Mar 13 '20 at 12:29
1

Looks like it should give an error - it has no way of knowing that you passed in a constant value as t to func.

More generally, you can't use runtime values as template arguments. Templates are inherently a compile-time construct.

Stuart Golodetz
  • 20,238
  • 4
  • 51
  • 80
  • 2
    And `constexpr` functions can be evaluated at compile time. – Xeo Jan 28 '12 at 13:42
  • 3
    @Xeo I don't see the relevance of your comment to this answer. It sounds like, I say "My hand cannot be used to walk around." and you say "And your arm can be used to move your hand." – Johannes Schaub - litb Jan 28 '12 at 13:47
  • 3
    @Johannes: Well, since `constexpr` functions can be evaluated at compile time, their arguments may to be known at compile-time, and as such they *could* be used as template arguments - if we could specify that we *only* allow compile-time values as arguments. Sadly, `constexpr void f(constexpr int x);` doesn't work. I know that you could just say `template constexpr void f();` at that point, but still... Template arguments are not subject to conversions, for example. – Xeo Jan 28 '12 at 13:56
  • 1
    @Xeo it seems you should have put these excellent comments as comments to the question. – Johannes Schaub - litb Jan 28 '12 at 14:01