Compilation error in a simple function template [duplicate] - c++

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.

Related

Adding a type to an existing template with out it being dropped c++17

My goal is to be able to include my own extra type at declaration and have it passed to my template function. How would I be able to declare my type so that the compiler would not drop my extra template parameter.
For example I have this code:
#include <iostream>
#include <vector>
// my_vector is an alias for std::vector<T> that also takes an extra type E
template<typename T, typename E>
using my_vector = std::vector<T>;
// my aliased type is being demoted to std::vector<T, std::allocator<T> >
template<typename T, typename E>
void write_to(std::ostream stream, const my_vector<T, E>& vec) {
// I need E type for somthing here for example this
stream << static_cast<E>(vec.size());
for (auto elm : vec) {
stream << elm;
}
}
int main() {
// very redundantly declaring that I want my_vector
my_vector<float, uint8_t> vec = my_vector<float, uint8_t>{ 1.0f, 2.0f, 3.0f };
write_to(std::cout, vec);
// this would compile if I called write_to<uint8_t> but I want this to be assumed by the compiler
}
g++ output suggests that it is not passing my_vector<T, E> to write_to but instead drops my_vector altogether and instead passes std::vector<T, std::allocator<T> >, is it possible to get the compiler to not drop the extra template parameter so that I dont have to explicitly include it in every call of write_to here?
Here is my output from g++ std=c++17
[arkav:~/devel/packetize] $g++ template_demote.cc --std=c++17
template_demote.cc: In function ‘int main()’:
template_demote.cc:21:25: error: no matching function for call to ‘write_to(std::ostream&, my_vector<float, unsigned char>&)’
21 | write_to(std::cout, vec); // this would compile if I called write_to<uint8_t> but I want this to be assumed by the compiler
| ^
template_demote.cc:10:6: note: candidate: ‘template<class T, class E> void write_to(std::ostream, my_vector<T, E>&)’
10 | void write_to(std::ostream stream, const my_vector<T, E>& vec) {
| ^~~~~~~~
template_demote.cc:10:6: note: template argument deduction/substitution failed:
template_demote.cc:21:25: note: couldn’t deduce template parameter ‘E’
21 | write_to(std::cout, vec); // this would compile if I called write_to<uint8_t> but I want this to be assumed by the compiler
| ^
Solution
Inherent std::vector and its constructor in my type definition
template<typename E, typename T>
class my_vector: public std::vector {
using std::vector<T>::vector;
};
You can try something like this:
#include <iostream>
#include <vector>
template<class T, class E>
struct util {
std::vector<T> my_vector;
void write_to(std::ostream& stream) {
stream << static_cast<E>(my_vector.size());
for (auto elm : my_vector) {
stream << elm;
}
}
};
int main() {
util<float, uint8_t> u;
u.my_vector = std::vector<float>{ 1.0f, 2.0f, 3.0f };
u.write_to(std::cout);
}
You can read in cppreference that, when aliasing:
Alias templates are never deduced by template argument deduction when deducing a template template parameter.

Class vs. function template specialization [duplicate]

This question already has answers here:
C++ template specialization for pointer?
(3 answers)
Closed 2 years ago.
I only recently learned about partial template specialization in C++ from here, and it perfectly solved a problem where I needed a template class to behave differently for pointers and non-pointers. A simplified example is:
// main.cpp
template <typename T>
class C
{
public:
C( const T& t ) : t_( t ) {}
private:
T t_;
};
template <typename T>
class C<T*>
{
public:
C( const T& t ) : t_( new T(t) ) {}
~C() { delete t_; }
private:
T* t_;
};
int main( int argc, char* argv[] )
{
C<int> c1(4);
C<int*> c2(2);
return 0;
}
I wanted to try extend this idea to functions, but ran into compiler errors I didn't understand:
// main.cpp
#include <set>
template <typename T>
std::set<T> makeSet( size_t off )
{
std::set<T> s;
s.insert( T() + T(off) );
return s;
}
template <typename T>
std::set<T*> makeSet<T*>( size_t off )
{
std::set<T*> s;
T* t = new T( T() + T(off) );
s.insert( t );
return s;
}
int main( int argc, char* argv[] )
{
std::set<int> s1 = makeSet<int>(4);
std::set<int*> s2 = makeSet<int*>(2);
delete *(s2.begin());
return 0;
}
.
$ g++ --version && g++ -g ./main.cpp
g++ (GCC) 9.2.1 20190827 (Red Hat 9.2.1-1)
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
./main.cpp:13:38: error: non-class, non-variable partial specialization ‘makeSet<T*>’ is not allowed
13 | std::set<T*> makeSet<T*>( size_t off )
| ^
./main.cpp: In function ‘int main(int, char**)’:
./main.cpp:23:37: error: call of overloaded ‘makeSet<int>(int)’ is ambiguous
23 | std::set<int> s1 = makeSet<int>(4);
| ^
./main.cpp:5:13: note: candidate: ‘std::set<T> makeSet(size_t) [with T = int; size_t = long unsigned int]’
5 | std::set<T> makeSet( size_t off )
| ^~~~~~~
./main.cpp:13:14: note: candidate: ‘std::set<T*> makeSet(size_t) [with T = int; size_t = long unsigned int]’
13 | std::set<T*> makeSet<T*>( size_t off )
| ^~~~~~~~~~~
./main.cpp:24:38: error: call of overloaded ‘makeSet<int*>(int)’ is ambiguous
24 | std::set<int*> s2 = makeSet<int*>(2);
| ^
./main.cpp:5:13: note: candidate: ‘std::set<T> makeSet(size_t) [with T = int*; size_t = long unsigned int]’
5 | std::set<T> makeSet( size_t off )
| ^~~~~~~
./main.cpp:13:14: note: candidate: ‘std::set<T*> makeSet(size_t) [with T = int*; size_t = long unsigned int]’
13 | std::set<T*> makeSet<T*>( size_t off )
| ^~~~~~~~~~~
I was a little skeptical about this because it looks like an attempt at overloading a function with a different return-type but with the same arguments - I know this is otherwise illegal, but I thought that because these were template functions, specifying the template type when invoking the functions (as in the above main()) would allow disambiguation.
But I'm not sure that's what the compiler is complaining about on line 13: what does the non-class, non-variable partial specialization ‘makeSet<T*>’ is not allowed error mean?
Is what I'm trying to do even possible in C++? I.e. can a template function be made so that it behaves differently for pointers and non-pointer template types and return a STL container of the specified type? (Would the answer be different between C++ 98, 03, 11, and 14?)
what does the non-class, non-variable partial specialization ‘makeSet<T*>’ is not allowed error mean?
Let's take a look at some documentation from cppreference.com regarding partial template specialization:
Allows customizing class [and variable (since C++14)] templates for a given category of template arguments.
Partial specializations are allowed for class and variable templates. The compiler has told you that your template is for neither a class for a variable. Therefore, partial specialization is not allowed.
A less confusing wording for the compiler's message would be: partial specialization of function templates is not allowed.
As for what can be done, it is possible to declare a class template with a suitable operator(). In some cases this is about as good as using a function template.
You can try something like this (C++17):
template <typename T>
std::set<T> makeSet( size_t off )
{
std::set<T> s;
if constexpr(std::is_pointer_v<T>)
{
using U = std::remove_reference_t<decltype(*std::declval<T>())>;
s.insert(new U( U() + U(off) ));
}
else
{
s.insert( T() + T(off) );
}
return s;
}

Access to private tuple element through a template member function [duplicate]

This question already has answers here:
Where and why do I have to put the "template" and "typename" keywords?
(8 answers)
Closed 6 years ago.
The class foo contains a private tuple member. I want to get reference to an element of this tuple using a getElement<I>(). I came to this solution but it doesn't work when the object is passed to the constructor of another class bar:
#include <tuple>
template<class... Args>
class foo {
std::tuple<Args...> tup_;
public:
foo(Args... args) : tup_ {args...} {};
template<size_t I>
const typename std::tuple_element<I, std::tuple<Args...>>::type &
getElement() const {return std::get<I>(tup_);}
};
template<class T>
class bar;
template<class T, class U>
class bar<foo<T,U>> {
public:
bar(foo<T,U> f) {
auto j = f.getElement<0>(); // this is an ERROR!!! Line 22
}
};
int main()
{
foo<int, char> f(12,'c');
auto j = f.getElement<0>(); // but this is OK!
bar<decltype(f)> b(f);
return 0;
}
compiler output:
main.cpp: In constructor 'bar<foo<T, U> >::bar(foo<T, U>)':
main.cpp:22:33: error: expected primary-expression before ')' token
auto j = f.getElement<0>(); // this is an ERROR!!!
^
main.cpp: In instantiation of 'bar<foo<T, U> >::bar(foo<T, U>) [with T = int; U = char]':
main.cpp:32:24: required from here
main.cpp:22:29: error: invalid operands of types '<unresolved overloaded function type>' and 'int' to binary 'operator<'
auto j = f.getElement<0>(); // this is an ERROR!!!
You must warn the compiler that getElement is a template method. And to do it you must specify the template keyword, eg:
f.template getElement<0>()
This because otherwise the compiler tries to parse the code as f.getElement < 0 so that it tries to call the binary operator< on f.getElement and 0 which is not what you want to do.

why this piece code(some template stuff) compiled well in msvc10 but compiled failed in gcc4.8.1? [duplicate]

This question already has answers here:
Where and why do I have to put the "template" and "typename" keywords?
(8 answers)
Closed 9 years ago.
#include <iostream>
#include <typeinfo>
using namespace std;
struct mystruct{};
template<typename T>
struct map;
//specification
#define MAPPING(Key, Val) \
template<> \
struct map<Key> \
{ \
typedef Val mapping_type; \
};
MAPPING(mystruct, int)
template<typename T>
void func(T t)
{
map<T>::mapping_type i = 999;
cout<<i<<endl;
}
int main() {
// your code goes here
mystruct ms;
func(ms);
return 0;
}
I try to do some type mapping(here mapping mystruct to int) via specification, but it can't compiled by gcc4.8.1, help!
or what is the right way to accomplish this, thanks!
http://ideone.com/yefbtk
Here are the error messages:
prog.cpp: In function ‘void func(T)’:
prog.cpp:23:2: error: need ‘typename’ before ‘map<T>::mapping_type’ because ‘map<T>’ is a dependent scope
map<T>::mapping_type i = 999;
^
prog.cpp:23:23: error: expected ‘;’ before ‘i’
map<T>::mapping_type i = 999;
^
prog.cpp:24:8: error: ‘i’ was not declared in this scope
cout<<i<<endl;
^
prog.cpp: In instantiation of ‘void func(T) [with T = mystruct]’:
prog.cpp:31:9: required from here
prog.cpp:23:2: error: dependent-name ‘map<T>::mapping_type’ is parsed as a non-type, but instantiation yields a type
map<T>::mapping_type i = 999;
^
prog.cpp:23:2: note: say ‘typename map<T>::mapping_type’ if a type is meant
As Mat pointed out in the comments, you must include typename before using map::mapping_type.
template<typename T>
void func(T t)
{
typename map<T>::mapping_type i = 999;
cout<<i<<endl;
}

Using a templated parameter's value_type

How is one supposed to use a std container's value_type?
I tried to use it like so:
#include <vector>
using namespace std;
template <typename T>
class TSContainer {
private:
T container;
public:
void push(T::value_type& item)
{
container.push_back(item);
}
T::value_type pop()
{
T::value_type item = container.pop_front();
return item;
}
};
int main()
{
int i = 1;
TSContainer<vector<int> > tsc;
tsc.push(i);
int v = tsc.pop();
}
But this results in:
prog.cpp:10: error: ‘T::value_type’ is not a type
prog.cpp:14: error: type ‘T’ is not derived from type ‘TSContainer<T>’
prog.cpp:14: error: expected ‘;’ before ‘pop’
prog.cpp:19: error: expected `;' before ‘}’ token
prog.cpp: In function ‘int main()’:
prog.cpp:25: error: ‘class TSContainer<std::vector<int, std::allocator<int> > >’ has no member named ‘pop’
prog.cpp:25: warning: unused variable ‘v’
I thought this was what ::value_type was for?
You have to use typename:
typename T::value_type pop()
and so on.
The reason is that the compiler cannot know whether T::value_type is a type of a member variable (nobody hinders you from defining a type struct X { int value_type; }; and pass that to the template). However without that function, the code could not be parsed (because the meaning of constructs changes depending on whether some identifier designates a type or a variable, e.g.T * p may be a multiplication or a pointer declaration). Therefore the rule is that everything which might be either type or variable and is not explicitly marked as type by prefixing it with typename is considered a variable.
Use the typename keyword to indicate that it's really a type.
void push(typename T::value_type& item)
typename T::value_type pop()
Here is a full implementation of the accepted answers above, in case it helps anyone.
#include <iostream>
#include <list>
template <typename T>
class C1 {
private:
T container;
typedef typename T::value_type CT;
public:
void push(CT& item) {
container.push_back(item);
}
CT pop (void) {
CT item = container.front();
container.pop_front();
return item;
}
};
int main() {
int i = 1;
C1<std::list<int> > c;
c.push(i);
std::cout << c.pop() << std::endl;
}
A fairly common practice is to provide an alias representing the underlying value type for convenience.
template <typename T>
class TSContainer {
private:
T container;
public:
using value_type = typename T::value_type;
void push(value_type& item)
{
container.push_back(item);
}
value_type pop()
{
value_type item = container.pop_front();
return item;
}
};