Why does Clang++ compiler fail to compile the following variadic template code? - c++

#include <string>
#include <iostream>
#include <tuple>
#include <utility>
template<typename... T> struct test {
using args_type = std::tuple<T...>;
args_type x;
template<std::size_t... I>
void callme(std::index_sequence<I...>) {
int _[] = {(std::get<I>(x).std::tuple_element<I, args_type>::type::~type(), true)...};
}
};
int main() {
}
The error message is
clang-3.7 -std=gnu++1y -Wc++14-extensions test.cpp
test.cpp:15:56: error: expected ')'
int _[] = {(std::get<I>(x).std::tuple_element<I, args_type>::type::~type(), true)...};
^
test.cpp:15:20: note: to match this '('
int _[] = {(std::get<I>(x).std::tuple_element<I, args_type>::type::~type(), true)...};
^
1 error generated.
The same code seems to compile just fine with G++ 4.9.2. I couldn't find any relevant bug report on Clang yet.

Appears to be a Clang bug, though the lookup of such pseudo-destructor-names is probably defected and subject of open CWG issues, specifically 555 and 399.
The significant bit of the expansion pattern is
std::get<I>(x).std::tuple_element<I, args_type>::type::~type()
Here, the bit between . and () is a pseudo-destructor-name; Qualified name lookup then mandates that
If a pseudo-destructor-name (5.2.4) contains a nested-name-specifier,
the type-names are looked up as types in the scope designated by the
nested-name-specifier. Similarly, in a qualified-id of the form:
nested-name-specifieropt class-name :: ~ class-name
the second class-name is looked up in the same scope as the first.
I.e. type is looked up in std::tuple_element<I, args_type>, where it's found to refer to some type. Note that class-name is a grammatical name for identifiers (and simple-template-ids), and need not refer to an actual class. std::get<I>(x).std::tuple_element<I, args_type>::type::~type then refers to the destructor of type.
Workaround with an auxiliary function:
template <typename T>
void destroy(T& p) {p.~T();}
template<typename... T> struct test {
using args_type = std::tuple<T...>;
args_type x;
template<std::size_t... I>
void callme(std::index_sequence<I...>) {
int _[] = {(destroy(std::get<I>(x)), 0)...};
}
};

Interestingly, hstong from IBM mentioned a couple workarounds that work better.
int _[] = {(std::get<I>(x).::std::tuple_element<I, args_type>::type::~type(), true)...};
or
int _[] = {(std::get<I>(x).std::template tuple_element<I, args_type>::type::~type(), true)...};

Related

Compilation error in a simple function template [duplicate]

This question already has answers here:
Where and why do I have to put the "template" and "typename" keywords?
(8 answers)
Closed last year.
While experimenting with this stackoverflow answer I encountered a compilation error I don't understand.
With #if 1 the compilation fails with following error log whereas with if 0 the compilation is OK.
Full error log:
Output of x86-64 gcc 11.2 (Compiler #1)
<source>: In function 'void remove(std::vector<T>&, size_t)':
<source>:8:3: error: need 'typename' before 'std::vector<T>::iterator' because 'std::vector<T>' is a dependent scope
8 | std::vector<T>::iterator it = vec.begin();
| ^~~
<source>:8:27: error: expected ';' before 'it'
8 | std::vector<T>::iterator it = vec.begin();
| ^~~
| ;
<source>:9:16: error: 'it' was not declared in this scope; did you mean 'int'?
9 | std::advance(it, pos);
| ^~
| int
<source>: In instantiation of 'void remove(std::vector<T>&, size_t) [with T = int; size_t = long unsigned int]':
<source>:25:9: required from here
<source>:8:19: error: dependent-name 'std::vector<T>::iterator' is parsed as a non-type, but instantiation yields a type
8 | std::vector<T>::iterator it = vec.begin();
| ^~~~~~~~
<source>:8:19: note: say 'typename std::vector<T>::iterator' if a type is meant
Code (available here):
#include <iostream>
#include <vector>
#if 1
template <typename T>
void remove(std::vector<T>& vec, size_t pos)
{
std::vector<T>::iterator it = vec.begin();
std::advance(it, pos);
vec.erase(it);
}
#else
template <typename T>
void remove(std::vector<T>& vec, size_t pos)
{
vec.erase(vec.begin() + pos);
}
#endif
int main()
{
std::vector<int> myvector{ 1,2,3,4 };
remove(myvector, 2);
for (auto element : myvector)
std::cout << ' ' << element;
std::cout << '\n';
return 0;
}
Now if I do what the compiler suggests (typename std::vector<T>::iterator it = vec.begin();) it compiles, but I don't really understand why typename is required here.
The error message says it all:
error: dependent-name 'std::vector<T>::iterator' is parsed as a non-type, but instantiation yields a type
I.e., whilst for you as a programmer it is apparent that std::vector<T>::iterator is a type, for the compiler it is not, and the lack of a leading typename means it parses the dependent-name iterator as a non-type, but when instantiating the function template and thus its blueprinted definition for T as int, std::vector<T>::iterator is resolved as the (member alias declaration) type std::vector<int>::iterator.
Whilst P0634R3 (Down with typename!), introduced for C++20:
[...] removes the need to disambiguate a dependent name as a typename via the typename keyword from several places where this is already unambiguous
the example above is not such a place/context. To understand why the compiler cannot resolve this unambiguously for all T's, see the example in the end of this answer.
If anything this is a compilation error resulting from a verbose approach to the function's definition. There is no need to include a dependent name in the declaration of the iterator variable:
void remove(std::vector<T>& vec, size_t pos)
{
auto it = vec.begin();
std::advance(it, pos);
vec.erase(it);
}
template<typename T>
struct Evil {
using iterator = T*;
};
template<>
struct Evil<int> {
static constexpr int iterator{42};
};
template<typename T>
void f() {
static_cast<void>(Evil<T>::iterator);
}
int main() {
f<int>(); // well-formed by evil explicit specialization
f<char>(); // ill-formed by primary template where `iterator` is a type
// error: missing 'typename' prior to dependent type name 'Evil<char>::iterator'
}
Or, courtesy of #Jarod42, even more evil where each case might work but do entirely different things.
template <bool isType>
struct Evil {
using var = int;
};
template<>
struct Evil<false> {
static constexpr int var {42};
};
template<bool isType>
void f()
{
[[maybe_unused]]int b = 42;
if constexpr (isType)
{
[[maybe_unused]] typename
Evil<isType>::var * b; // is it a pointer declaration shadowing b
// or a multiplication
} else {
Evil<isType>::var * b; // is it a pointer declaration shadowing b
// or a multiplication
}
}
int main()
{
f<true>();
f<false>();
}
DEMO.

Constraints not satisfied for template template concept requiring static template method

I'm trying to implement Functor and various other category-theoretic concepts using C++ concepts, but am getting compile errors:
http://coliru.stacked-crooked.com/a/e8b6eb387229bddf
Here's my full code (I know that requiring fmap<int, int> does not verify fmap for any two types, and I plan to change it to fmap<int, std::string> or something to achieve a slightly stronger test -- or instead, possibly alter the Functor concept so that it takes in addition to F, two types T and U and verifies the existence of fmap<T, U>, but that's all after I figure out how to fix the error that I'm getting):
#include <functional>
#include <iostream>
#include <vector>
// empty Functor_Impl struct - specialize for each functor
template<template<class> class F> struct Functor_Impl {};
// std::vector Functor implementation
template<>
struct Functor_Impl<std::vector> {
template<class T, class U>
static std::vector<U> fmap(std::vector<T> x, std::function<U(T)> f) {
std::vector<U> out;
out.reserve(x.size());
for (int i = 0; i < x.size(); i++) {
out.push_back(f(x[i]));
}
return out;
}
};
// Functor concept requires Functor_Impl<F> to have fmap
template<template<class> class F>
concept bool Functor = requires(F<int> x) {
{Functor_Impl<F>::template fmap<int, int>(x)} -> F<int>;
};
// Test function using constraint.
template<template<class> class F, class T>
requires Functor<F>
F<T> mult_by_2(F<T> a) {
return Functor_Impl<F>::template fmap<T, T>(a, [](T x) {
return x * 2;
});
}
int main() {
std::vector<int> x = {1, 2, 3};
std::vector<int> x2 = mult_by_2(x);
for (int i = 0; i < x2.size(); i++) {
std::cout << x2[i] << std::endl;
}
}
And the compile error:
lol#foldingmachinebox:~/p/website-editor$ g++ foo.cpp -std=c++17 -fconcepts -o foo
foo.cpp: In function ‘int main()’:
foo.cpp:39:38: error: cannot call function ‘F<T> mult_by_2(F<T>) [with F = std::vector; T = int]’
std::vector<int> x2 = mult_by_2(x);
^
foo.cpp:31:6: note: constraints not satisfied
F<T> mult_by_2(F<T> a) {
^~~~~~~~~
foo.cpp:24:14: note: within ‘template<template<class> class F> concept const bool Functor<F> [with F = std::vector]’
concept bool Functor = requires(F<int> x) {
^~~~~~~
foo.cpp:24:14: note: with ‘std::vector<int> x’
foo.cpp:24:14: note: the required expression ‘Functor_Impl<F>::fmap<int, int>(x)’ would be ill-formed
I'm guessing that my syntax for the concept itself is wrong - that it's treating a variable as a function, or vice versa, since I'm not very familiar with the concept syntax, and in addition some of the example code on cppreference.com does not compile under GCC's implementation (e.g. concept EqualityComparable does not compile, it must be changed to concept bool EqualityComparable).
If I remove requires Functor<F> from the mult_by_2 function declaration, then the code compiles and runs.
The problem is exactly what the error message says: Functor_Impl<F>::template fmap<int, int>(x) is not a valid expression. Functor_Impl<std::vector>::fmap has two parameters, not one.

Using decltype with an operator using non-ADL lookup

I have the following (contrived) code:
#include <utility>
namespace N
{
struct A {};
struct B {};
}
namespace operators
{
bool operator|(N::A, N::B) { return true; };
}
namespace details
{
using namespace operators;
template<typename T, typename U>
using UnionResultType = decltype(std::declval<T>() | std::declval<U>());
}
int main()
{
{
using namespace operators;
using UnionAB = decltype(std::declval<N::A>() | std::declval<N::B>());
static_assert(std::is_same<UnionAB, bool>::value);
}
{
using UnionAB = details::UnionResultType<N::A, N::B>;
}
return 0;
}
GCC has a problem compiling the UnionAB declaration in the second block:
operators.cpp: In substitution of ‘template<class T, class U> using UnionResultType = decltype
((declval<T>() | declval<U>())) [with T = N::A; U = N::B]’:
operators.cpp:31:56: required from here
operators.cpp:19:54: error: no match for ‘operator|’ (operand types are ‘N::A’ and ‘N::B’)
using UnionResultType = decltype(std::declval<T>() | std::declval<U>());
Even though there is a using namespace operators within the scope defining the templated UnionResultType, GCC fails to find the operator overload.
I am using GCC 7.2.0 (C++11, 14, and 17 mode all have the same problem).
Is there some issue in the code, or is this a compiler issue? In case this is a compiler issue, is this a known issue?

How to declare a self-referential template typedef

Here's a small example which is substantially similar to what I'm trying to do:
#include <boost/variant/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <utility>
#include <vector>
struct foo {
const char * str;
};
typedef std::pair<float, float> fpair;
//typedef std::vector<boost::variant<int, fpair, foo, vlist>> vlist;
// ^ No...
//typedef std::vector<boost::variant<int, fpair, foo, boost::recursive_wrapper<vlist>>> vlist;
// ^ No...
//template <typename T = vlist<T> >
//using vlist = std::vector<boost::variant<int, fpair, foo, boost::recursive_wrapper<vlist>>>;
// ^ No...
template <typename T = vlist<T> >
using vlist = std::vector<boost::variant<int, fpair, foo, boost::recursive_wrapper<T>>>;
// Still no?
int main () {
std::cout << "Hello world\n";
}
The error I get with gcc 4.8 is:
test.cpp:12:33: error: expected nested-name-specifier before ‘vlist’
template <typename T = typename vlist<T>>
^
test.cpp:12:33: error: expected ‘>’ before ‘vlist’
The error with clang 3.6 is:
test.cpp:12:24: error: unknown type name 'vlist'
template <typename T = vlist<T>>
^
test.cpp:12:29: error: expected ',' or '>' in template-parameter-list
template <typename T = vlist<T>>
^
test.cpp:12:32: error: expected unqualified-id
template <typename T = vlist<T>>
^
3 errors generated.
(Edit: actually these errors are from slightly different versions of the above code, but they all give quite similar messages)
I looked at these earlier, slightly different questions, I'm still stumped:
How to declare a self referencing template type
How to properly declare a self-referencing template type?
Boost Fusion adapt declaration for a templated self referential structure
Does anyone know a trick for this, or is there some reason I'm not aware of that the compiler inherently isn't able to do this?
I believe you just want boost::make_recursive_variant:
#include <boost/variant/variant.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <utility>
#include <vector>
struct foo {
const char* str;
};
typedef std::pair<float, float> fpair;
typedef boost::make_recursive_variant<
int,
fpair,
foo,
std::vector<boost::recursive_variant_>
>::type vlist;
int main() {
std::vector<vlist> vec;
vec.push_back(4);
vec.push_back(fpair{1.0f, 2.0f});
vlist v2(vec);
}

Template functions in namespace cause errors

Assume the following code:
#include <iostream>
template<typename T>
struct Link
{
Link(T&& val) : val(std::forward<T>(val)) {}
T val;
};
template<typename T>
std::ostream& operator<<(std::ostream& out, const Link<T>& link)
{
out << "Link(" << link.val << ")";
return out;
}
template<typename T>
auto MakeLink(T&& val) -> Link<T>
{
return {std::forward<T>(val)};
}
namespace Utils {
template<typename Any>
constexpr auto RemoveLinks(const Any& any) -> const Any&
{
return any;
}
template<typename T>
constexpr auto RemoveLinks(const Link<T>& link) -> decltype(RemoveLinks(link.val))
{
return RemoveLinks(link.val);
}
} /* Utils */
int main()
{
int k = 10;
auto link = MakeLink(MakeLink(k));
std::cout << link << std::endl;
std::cout << Utils::RemoveLinks(link) << std::endl;
}
For some reason I can't understand, it generates the following compilation errors with g++-4.8:
/home/allan/Codes/expr.cpp: In instantiation of ‘constexpr decltype (Utils::RemoveLinks(link.val)) Utils::RemoveLinks(const Link<T>&) [with T = int&; decltype (Utils::RemoveLinks(link.val)) = const int&]’:
/home/allan/Codes/expr.cpp:88:32: required from ‘constexpr decltype (Utils::RemoveLinks(link.val)) Utils::RemoveLinks(const Link<T>&) [with T = Link<int&>; decltype (Utils::RemoveLinks(link.val)) = const int&]’
/home/allan/Codes/expr.cpp:100:41: required from here
/home/allan/Codes/expr.cpp:88:32: error: invalid initialization of reference of type ‘const Link<int&>&’ from expression of type ‘const int’
return RemoveLinks(link.val);
^
/home/allan/Codes/expr.cpp:89:1: error: body of constexpr function ‘constexpr decltype (Utils::RemoveLinks(link.val)) Utils::RemoveLinks(const Link<T>&) [with T = int&; decltype (Utils::RemoveLinks(link.val)) = const Link<int&>&]’ not a return-statement
}
^
/home/allan/Codes/expr.cpp: In function ‘constexpr decltype (Utils::RemoveLinks(link.val)) Utils::RemoveLinks(const Link<T>&) [with T = int&; decltype (Utils::RemoveLinks(link.val)) = const int&]’:
/home/allan/Codes/expr.cpp:89:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
while clang 3.3 gives:
test.cc:34:12: error: reference to type 'const Link<int &>' could not bind to an lvalue of type 'const int'
return RemoveLinks(link.val);
^~~~~~~~~~~~~~~~~~~~~
test.cc:46:25: note: in instantiation of function template specialization 'Utils::RemoveLinks<Link<int &> >' requested here
std::cout << Utils::RemoveLinks(link) << std::endl;
If, however, the namespace Utils is removed, then it compiles without errors (both gcc and clang), and execution outputs:
Link(Link(10))
10
Why defining those template functions (RemoveLinks) in a namespace causes such errors?
This problem is a result of an issue with the point of declaration (1) combined with dependent name lookup (2).
(1) In the declaration
template<typename T>
constexpr auto RemoveLinks(const Link<T>& link) -> decltype(RemoveLinks(link.val))
the name RemoveLinks, or more precisely, this overload of RemoveLinks, is only visible after the complete declarator according to [basic.scope.pdecl]/1. The trailing-return-type is part of the declarator as per [dcl.decl]/4. Also see this answer.
(2) In the expression RemoveLinks(link.val), the name RemoveLinks is dependent as per [temp.dep]/1, as link.val is dependent.
If we now look up how dependent names are resolved, we find [temp.dep.res]:
In resolving dependent names, names from the following sources are considered:
Declarations that are visible at the point of definition of the template.
Declarations from namespaces associated with the types of the function arguments both from the instantiation context and from the definition context.
The first bullet doesn't find the second overload of RemoveLinks because of the point of declaration (1). The second one doesn't find the overload because the namespace Util is not associated with any argument. This is why putting everything in the global namespace or in the namespace Util works as expected (Live example).
For the same reason, using a qualified-id in the trailing-return-type (like -> decltype(Util::RemoveLinks(link.val)) doesn't help here.
I tried compiling the sample code above with GCC 4.8.1, with clang and also Intel icpc and got the same error messages as you.
I am able to get it to successfully compile without trouble if I revise the signature of the template specialization from:
template<typename T>
constexpr auto RemoveLinks(const Link<T>& link) -> decltype(RemoveLinks(link.val))
and make the return type as const. This might cause a compiler warning since the const there is meaningless, but that can be ignored. I tested it and it works fine for me with gcc, but not icpc or clang:
template<typename T>
constexpr auto RemoveLinks(const Link<T>& link) -> decltype(RemoveLinks(link.val)) const
I found the error message (with the original code) from Intel icpc to be the most informative:
code.cc(48): error: template instantiation resulted in unexpected function type of "auto (const Link<Link<int &>> &)->const int &" (the meaning of a name may have changed since the template declaration -- the type of the template is "auto (const Link<T> &)->decltype((<expression>))")
std::cout << Utils::RemoveLinks(link) << std::endl;
^
detected during instantiation of "Utils::RemoveLinks" based on template argument <Link<int &>> at line 48
Unfortunately, the above answer is more of a workaround for gcc rather than an answer to your question. I'll update this if I have anything more / better to add.
EDIT
It appears that decltype(RemoveLinks(link.val)) is actually following the recursion so that it returns int& rather than Link.
EDIT #2
There have been reported bugs in LLVM about crashes caused by decltype recursion problems. It seems that this is definitely a bug of sorts, but one that seems to be present in multiple implementations of C++.
The problem can be fixed quite easily if you create a an alias for type T in the link struct and have decltype refer to the alias rather than to the return type. This will eliminate the recursion. As follows:
template<typename T>
struct Link
{
Link(T&& val) : val(std::forward<T>(val)) {}
using value_type = T;
T val;
};
And then the RemoveLinks signature is changed accordingly to refer to this alias:
template<typename T>
constexpr auto RemoveLinks(const Link<T>& link) -> decltype(links.value_type)
This code successfully builds on all 3 compilers.
I will file some bug reports with the compilers to see if there's anything they can do about it.
Hope this helps.