12

The following code compiles without problems in g++:

#include <iostream>
#include <string>
#include <tuple>

template<typename T>
void test(const T& value)
{
    std::tuple<int, double> x;
    std::cout << std::get<value>(x);
}

int main() {
    test(std::integral_constant<std::size_t,1>());
}

I used this command:

g++ test.cpp -o test -std=c++14 -pedantic -Wall -Wextra

But when I switch g++ to clang++ (with g++ 5.1.0 and clang++ 3.6.0), I get the following errors:

test.cpp:9:18: error: no matching function for call to 'get'
    std::cout << std::get<value>(x);
                 ^~~~~~~~~~~~~~~
test.cpp:13:5: note: in instantiation of function template specialization 'test<std::integral_constant<unsigned long, 1> >' requested here
    test(std::integral_constant<std::size_t,1>());
         ^~~~~~~~~~~~~~~
<skipped>

/usr/bin/../lib/gcc/x86_64-linux-gnu/5.1.0/../../../../include/c++/5.1.0/tuple:867:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Tp'
    get(tuple<_Types...>& __t) noexcept
    ^

And similar note: entries for other overloads of std::get.

But I'm passing std::integral_constant to test(), which is a constant-expression, why would it be an "invalid explicitly-specified argument" for the template parameter? Is it a clang bug or am I doing something wrong here?

I've noticed that if I change parameter for test() from const T& to const T, then clang compiles successfully. Do I somehow lose constexpr quality of integral_constant by passing it by reference?

Ruslan
  • 18,162
  • 8
  • 67
  • 136
  • Can `std::get` accept `std::integral_constant` as a template parameter at all? For example, [this](http://coliru.stacked-crooked.com/a/81a5121c07803176) is rejected by gcc. – Petr Nov 23 '15 at 13:27
  • And why don't you just pass `startingIndex` to `operation` instead of wrapping it into `std::integral_constant`? – Petr Nov 23 '15 at 13:29
  • @Petr you forgot to instantiate it. There's a `constexpr` conversion operator. – Ruslan Nov 23 '15 at 13:29
  • @Petr for the second comment, function arguments can't be `constexpr`. Hmm... I seem to start losing my understanding here... `integral_constant` is also an argument, why does gcc compile it?.. – Ruslan Nov 23 '15 at 13:30
  • Ok, [this](http://coliru.stacked-crooked.com/a/1980cd7bb7b1162a) does not work either – Petr Nov 23 '15 at 13:31
  • 1
    @Petr Indeed... but passing `integral_constant` to a separate template function seems to do: [here](http://coliru.stacked-crooked.com/a/87e30aa1251e3cdb). – Ruslan Nov 23 '15 at 13:37
  • And your last example is rejected by clang for me. Seems that this is a simplified version of your problem. – Petr Nov 23 '15 at 13:47
  • @Petr which version of clang do you use? Yes, it indeed seems to be a simplified version. – Ruslan Nov 23 '15 at 13:52
  • FWIW my clang compiles it if `index` is declared as a non-reference. – n. m. could be an AI Nov 23 '15 at 13:57
  • @n.m. yeah, I've mentioned it in the OP. – Ruslan Nov 23 '15 at 13:57
  • I suggest you edit the post with the new example (and remove original example), because the new one is much easier to comprehend and it seems to be the same issue. If after the new one is resolved you find that the original one is different, you can post a new question. – Petr Nov 23 '15 at 14:02
  • ah yes didn't see it. – n. m. could be an AI Nov 23 '15 at 14:05
  • As a workaround, `std::get(x)` (explicitly access `value`'s value) seems to work in both gcc and clang, so that's some problem with implicit type conversion for template argument. – Petr Nov 23 '15 at 14:25

1 Answers1

6

As there is no answer for a week, I will post my vision. I am far from being an expert at language-laywering, actually I would consider myself a complete novice, but still. The following is based on my reading of standard, as well on my recent question.

So, first of all let's rewrite the code the following way:

struct A {
    constexpr operator int() const { return 42; }
};

template <int>
void foo() {}

void test(const A& value) {
    foo<value>();
}

int main() {
    A a{};
    test(a);
}

It exhibits the same behavior (builds with gcc and fails with similar error with clang), but:

  • is free from template type deduction at test(), to make sure the problem has nothing to do with type deduction,
  • uses 'mocks' instead of std members to make sure this is not a problem with their implementation,
  • and has an explicit variable a, not a temporary, to be explained later.

What does happen here? I will quote N4296.

We have a template foo with a non-type parameter.

[14.3.2(temp.arg.nontype)]/1:

A template-argument for a non-type template-parameter shall be a converted constant expression (5.20) of the type of the template-parameter.

So template argument, i.e. value, should be a converted constant expression of type int.

[5.20(expr.const)]/4:

A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only

  • user-defined conversions,
  • ... (irrelevant bullets dropped)

and where the reference binding (if any) binds directly.

Our expression (value) can be implicitly converted to type int, and the conversion sequence contains only user-defined conversions. So the two questions remain: whether "the converted expression is a constant expression" and whether "the reference binding (if any) binds directly".

For the first question, the phrase "the converted expression", I think, means the expression as already converted to int, that is something like static_cast<int>(value), not the original expression (value). For that,

[5.20(expr.const)]/2:

A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:

  • ... (a long list omitted)

Evaluation of our expression, static_cast<int>(value), leads only to evaluation of A::operator int(), which is constexpr, and thus is explicitly allowed. No members of A (if there were any) are evaluated, neither anything else is evaluated.

Therefore, static_cast<int>(value) is a constant expression.

For the second question, about reference binding, it is not clear for me to which process this refers at all. However, anyway we have only one reference in our code (const A& value), and it binds directly to the variable a of main (and this is the reason why I introduced a).

Indeed, direct binding is defined at the end of [8.5.3(dcl.init.ref)]/5:

In all cases except the last (i.e., creating and initializing a temporary from the initializer expression), the reference is said to bind directly to the initializer expression.

This "last" case seem to refer to 5.2, and non-direct binding means initialization from a temporary (like const int& i = 42;), not our case when we have a non-temporary a.

UPD: I asked a separate question to check whether my understanding of the standard, presented above, is correct.


So the bottomline is that the code should be valid, and clang is wrong. I suggest you filing a bug to clang bug tracker, with a reference to this question. Or if for whatever reason you will not file a bug, let me know, I will file it.

UPD: filed a bug report

Community
  • 1
  • 1
Petr
  • 9,812
  • 1
  • 28
  • 52
  • 1
    As you seem to be much better than me at reading the Standard, I think it'd be better if you filed the bug report. (I'd like to have a link to follow it though if you do.) – Ruslan Dec 01 '15 at 11:18
  • @Ruslan, ok, but let's first wait for the results of [another my question](http://stackoverflow.com/questions/34018046/clarification-of-converted-constant-expression-definition) to check whether my reading is correct. – Petr Dec 01 '15 at 11:34
  • I just wanted to add that, a year later, as of Dec 2016, this bug is still present in `clang`. I'm running the bleeding edge of clang (`clang version 4.0.0-svn288882-1~exp1 (trunk)`), and I just encountered this bug. – Arjun Menon Dec 13 '16 at 07:41
  • There is no bug. This code snippet is ill-formed for violating [\[expr.const\]/2.11](https://timsong-cpp.github.io/cppwp/expr.const#2.11). Evaluating the equivalent of `static_cast(value)` necessitates evaluating the *id-expression* `value`. – T.C. Mar 27 '17 at 19:44
  • That doesn't matter. The thing that needs to be a constant expression is the equivalent of `static_cast(value)`, and the lifetime of the referent of `value` obviously didn't start within that expression. Pass by-value is supposed to work, so that doesn't prove anything. – T.C. Mar 28 '17 at 08:11
  • @T.C., but if I change `A a{};` to `constexpr A a{};` to make `value` be initialized with a constant expression (2.11.1), it also fails. – Petr Mar 28 '17 at 08:18
  • 1
    Actually, `value` doesn't have a "preceding initialization" at all. The definition of `test`, a non-template function, is either well-formed or ill-formed. That can't depend on how it is called. – T.C. Mar 28 '17 at 08:21
  • @T.C., I see, this make sense. – Petr Mar 28 '17 at 09:54
  • @T.C., would you post your answer? – Petr Mar 28 '17 at 09:55
  • Just passing by to say that this is still an issue in clang and msvc. – lightxbulb Oct 21 '19 at 16:16