Could someone explain why the following c++ code is not behaving as expected:
struct Object {
template< int i >
void foo(){ }
};
template<int counter>
struct Container {
Object v[counter];
void test(){
// this works as expected
Object a; a.foo<1>();
// This works as well:
Object *b = new Object(); b->foo<1>();
// now try the same thing with the array:
v[0] = Object(); // that's fine (just testing access to the array)
# if defined BUG1
v[0].foo<1>(); // compilation fails
# elif defined BUG2
(v[0]).foo<1>(); // compilation fails
# elif defined BUG3
auto &o = v[0];
o.foo<1>(); // compilation fails
# else
Object &o = v[0];
o.foo<1>(); // works
# endif
}
};
int main(){
Container<10> container;
}
The code above compiles fine without flag. If one of the flag BUG1 to BUG3 is set, the compilation fails with either GCC 4.6 or 4.7 and with clang 3.2 (which seems to indicate it is not a GCC bug).
Lines 21 to 29 are doing exactly the same thing semantically (ie calling a method of the first element of the Object array), but only the last version compiles. The problem only seems to arise when I try to call a templated method from a template object.
BUG1 is just the "normal" way of writing the call.
BUG2 is the same thing, but the array access is protected by parenthesis in case there was a precedence problem (but there shouldn't be any).
BUG3 shows that type inference is not working either (needs to be compiled with c++11 support).
The last version works fine, but I don't understand why using a temporary variable to store the reference solves the problem.
I am curious to know why the other three are not valid.
Thanks
You have to use template as:
v[0].template foo<1>();
auto &o = v[0];
o.template foo<1>();
Because the declaration of v depends on the template argument, which makes v a dependent name.
Here the template keyword tells compiler that whatever follows is a template (in your case, foo is indeed a template). If foo is not a template, then the template keyword is not required (in fact, it would be an error).
The problem is that o.foo<1>() can be parsed/interpreted in two ways: one is just as you expect (a function call), the other way is this:
(o.foo) < 1 //partially parsed
that is, foo is a member data (not function), and you've comparing it with 1. So to tell the compiler that < is not used to compare o.foo with 1, rather it is used to pass template argument 1 to the function template, you're required to use template keyword.
Inside templates, expressions can be type-dependent or value-dependent. From 14.6.2:
types and expressions may depend on the type and/or value of template parameters
In your case, counter is a template argument, and the declaration of v depends on it, making v[0] a value-dependent expression. Thus the name foo is a dependent-name which you must disambiguate as a template name by saying:
v[0].template foo<1>();
Related
The following code fails to compile
template<typename T>
struct A {
A(T) {}
};
int main() {
int x = 3;
A(x); // compile failure here
}
and the reasons seems to be explained here and here: A(x); is interpreted as A x;, a conflicting declaration of x.
This also explains one of clangd messages:
Candidate function template not viable: requires 1 argument, but 0 were provided
prova.cpp:8:7: error: no viable constructor or deduction guide for deduction of template arguments of 'A'
On the other hand, clangd also say:
Cannot use parentheses when declaring variable with deduced class template specialization type [deduced_class_template_compound_type]
What is this error actually referring to? I can only interpret it one way: without the parenthesis, I can provide the needed argument, e.g. A x{3};, but with the parenthesis I cannot declare a variable and also pass parameters to its constructor, e.g. A(x)(3); doesn't work, nor does A(x){3};.
Why this question?
In Chapter 11 from Functional Programming in C++, when exploring/explaining DSL, the author uses a template helper function to construct an object of a template class. The code, on the other hand, is assumed to be compiled with at least -std=c++17 (indeed, it makes use of other C++17-specific features, like fold expressions), so in principle the class can deduce the template type if a constructor taking an argument of that template type is provided, which is the case for the example. However, removing the helper function, and renaming the class to the name of the (removed) helper function, makes the new code fail to compile at the call site, where an object is constructed with the syntax className(objectOfOtherClass).
Let's take a template class:
template<typename T>
class A{};
And a helper function that instantiates an A with type deduction:
template<typename T>
constexpr A<T> makeA(T)
{
return A<T>();
}
And finally a runtime value, but whose type (int) is known at compile time:
int i = //... retrieving a runtime value;
The following does not compile, although it only uses the type of i, and not its value:
constexpr auto a = makeA(i);
The error being: the value of 'i' is not usable in a constant expression.
I understand that i can't be passed since its value is unknown at compile-time, but on the other hand makeA totally discards this value.
So how can I instantiate a constexpr A templated on the type of i, without having to spell out A<int> or A<decltype(i)>?
I'd imagine there must be a way since all the information is available at compile time.
While all the information is available at compile-time, you're trying to really make it look like you're passing an object to the function at runtime. There is simply no way to do this other than the alternative methods you've already listed.
makeA<int>() seems perfectly reasonable to me (if remarkably contrived, but I'll assume your real T is more complex), and is consistent with "maker" functions already provided by the standard library.
I have some templated code for timers abstraction on my avr device. Most relevant part here is:
template <typename T>
class Timerx8bit
{
T reg;
static_assert(T::res == TimerResolution::bit8, "The timer template is 8bit, registers must also be 8bit");
}
struct Timer0
{
constexpr static const TimerResolution res = TimerResolution::bit16;
volatile uint8_t* tccra = &TCCR0A;
volatile uint8_t* tccrb = &TCCR0B;
//[...]
};
Now I feed the Timer0 to the template. The problem with that code is, that the static assert seems to evaluate always to true, although in the above situation it should fail. TimerResolution is just an enum class.
The problem seems to be in the template, if I put "TimerResolution::bit8 != TimerResolution::bit8" as the condition, compilation fails as expected, but "T::res != T::res" compiles without any problems... what am I missing here?
EDIT:
While preparing full example of code I found the problem, although I still don't quite understand why it behaves that way. First, the code:
enum class TimerResolution_test
{
bit8,
bit16
};
struct Timer0_test
{
constexpr static const TimerResolution_test res = TimerResolution_test::bit8;
};
template <typename T>
class Timerx8bit_test
{
public:
constexpr static const TimerResolution_test res = TimerResolution_test::bit8;
private:
T reg;
static_assert(T::res != T::res, "The timer template is 8bit, registers must also be 8bit");
};
template<typename Timer>
class pwm_test
{
};
Instantiation:
pwm_test<Timerx8bit_test<Timer0_test>> testTimer; // Compiles
Timerx8bit_test<Timer0_test> testTimer2; // Fails
The second instantiation fails with the static_assert as above. If I put 'false' instead of the templated condition it fails in both cases... why is that? Shouldn't it fail in both cases with the original templated condition?
Templates don't require the complete type definition immediately (think CRTP). They must use the type in a way that will cause the complete type definition to be required. Your pwm_test doesn't use the type parameter it is given except by name. So the template body never needs to be instantiated.
In the second case you create an object, so naturally the template body instantiation happens.
So you need to force the instantiation by providing a context where it must happen:
template<typename Timer>
class pwm_test
{
enum { _ = sizeof(Timer) };
};
On a related note, if you have any static assertions in a member function of a template, those won't be triggered until you add a call to the function in your code.
To answer your other question, why does static_assert result in an immediate error in one case but not the other:
ยง14.6/8 [temp.res]
Knowing which names are type names allows the syntax of every template
to be checked. The program is ill-formed, no diagnostic required, if:
...
a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not
depend on a template parameter, or
So when encountering static_assert(false) while parsing the template, the compiler can deduce that all templates instantiations will be ill-formed. In this case it chooses to issue a diagnostic immediately (note that it doesn't have to).
When static_assert(T::res != T::res) is encountered, the compiler checks the syntax, but it cannot deduce that T::res != T::res is always false, that information is only available when T is known (after all, T::res can be something that overloads operator!= to always return true).
Consider the following example:
template <class T>
class C
{
public:
C();
C(C&& rhs);
private:
T m_data;
};
template <class T>
C<T>::C()
: m_data(T())
{
}
template <class T>
C<T>::C(C&& rhs)
: m_data(rhs.data)
{
}
int main()
{
C<int> i;
}
Line : m_data(rhs.data) contains an error as C does not have a member named data. But none of the compilers that I tried (gcc 5.2, clang 3.5.1) detected that error.
But when I add the following line to main function the compiler detects the error:
C<int> j = std::move(i);
Why the compiler does not give an error in the first case?
Even if that particular function is not called it can figure out that C has no member named data.
In addition when I change the definition of move constructor to the following:
template <class T>
C<T>::C(C&& rhs)
: m_data(rhs.data)
{
data = 0;
}
the compiler gives error on line data = 0; but not on : m_data(rhs.data).
So the function gets parsed.
There is 2 passes to check error in template.
One for non dependent code
One for dependent code (done at instantiation)
Your code is template dependent, so it is checked only when the method is instantiated.
Your template is ill-formed but the error does not require a diagnostic (in other words, the compiler is allowed to not give an error message and can do anything it wants). More precisely, the Standard says
Similarly, if the id-expression in a class member access expression for which the type of the object expression is the current instantiation does not refer to a member of the current instantiation or a member of an unknown specialization, the program is ill-formed even if the template containing the member access expression is not instantiated; no diagnostic required.
In your code C is the current instantiation and rhs.data is a class member access expression but does not refer to a member of the current instantiation and not to a member of an unknown specialization (which would be the case if C had dependent base classes, i.e if you would have written class C : T or something similar).
To read up on these rules, see this answer . It is also worth nothing that this kind of code has always been ill-formed (no diagnostic required), even in C++03 that didn't have this addition rule that I quoted. Because this code makes the template have no valid instantiation, for all possible types of T. But the existing rule of C++03 is rather broad and this addition of C++11 is a succinct test that allows this kind of code to be safely rejected.
the compiler does not try to compile C(C&& rhs); since you don't call it (in the first try).
this is called the zero overhead rule in C++ - you don't pay on what you don't use.
when you try to call it the compiler than tries to compile the function and fails.
In this case, the compiler obiously couldn't figure out that the move constructor will never work. It is allowed to do so, but not required.
In the general case, it is hard to detect that there is no T whatsoever for which the code could be compiled. If it just fails for C<int> and C<float>, but works for C<my_class>, the compiler must not complain (as long as the function isn't used for int or float).
With the definition shown below, I can call qget<0>() or qget<1>() using G++ (4.7.2), but qget<2> or "higher" will fail with a no matching function error. Clang++ (3.2), meanwhile, fails with any of them. I've used lazy enable_if as a last resort; though I don't think I should need it. I know the code looks a bit odd, but can anyone see the source of the error? (Boost provides the enable_if classes.)
template <typename T> struct Tid { typedef T type; };
template <unsigned I>
typename enable_if_c<(I==0),double>::type
qget()
{ return 0.0; }
template <unsigned I>
typename lazy_enable_if_c<(I!=0), Tid<decltype(qget<I-1>())>>::type
qget()
{ return qget<I-1>(); }
When you declare a function or function template as e.g. ret foo(A, B, C); or indifferently as auto foo(A, B, C) -> ret;, then the foo that refers to the entity just declared is not in scope until after the so-called declarator. In your particular case, the return type (be it a late return type or not) is always part of the declarator.
This means that in your last declaration the name qget in the return type may refer to the previous declaration (the case for I==0) but may never refer to the current declaration. This is why qget<0> and qget<1> are found, but qget<2> is not: when attempting to form the return type of the latter, qget<1> is not found because the first declaration is SFINAE'd out, as intended and the second declaration is the current declaration and not in scope. The error results in SFINAE.
My usual solution when this happens (which I have to say is not that often) is to use a struct (as an implementation detail) because all member functions (and member function templates) are declared inside the definition of a class, from the opening brace on.
That being said, you'll still hit a snag because even though you are using lazy_enable_if_c you're still eagerly computing the type of qget<I - 1>() (as a parameter to lazy_enable_if_c) even when I is 0. Lazily evaluating the identity of said type won't save you.
Unfortunately I can't seem to get an example running using GCC 4.7.2, which insists in not terminating the recursion even when fixing the condition to I > 0 and using a lazy result (although normally I'd use 4.8), so I can't promise you my solution can be made to work.