Why does gcc tell me that my template is not a template? - c++

I have such a piece of code:
template<template<typename> class ContainerType,
typename ValueType,
typename ReturnType>
struct value_extractor
{
public:
static ReturnType extract(const ContainerType<ValueType>&);
};
template<template<typename> class ContainerType,
typename ValueType>
struct value_extractor<ContainerType, ValueType, std::shared_ptr<ValueType>>
{
static std::shared_ptr<ValueType> extract(const ContainerType<ValueType>& value)
{
return value;
}
};
which essentially extracts value from a template type. This code compiles well using clang but with gcc I get an error saying:
g++ test.cpp -lstdc++ -O2
In file included from di.hpp:1:0,
from test.cpp:2:
holders.hpp: In instantiation of ‘ReturnType di::holder<ContainerType, ValueType>::provide() const [with ReturnType = std::shared_ptr<int>; ContainerType = std::shared_ptr; ValueType = int]’:
di.hpp:35:105: required from ‘static ReturnType di::holder_selector::convert(const types_map&, ContainerType<ValueType>*) [with ReturnType = std::shared_ptr<int>; ContainerType = std::shared_ptr; ValueType = int; di::types_map = std::unordered_map<void (*)(), std::unique_ptr<di::base_holder> >]’
di.hpp:40:39: required from ‘T di::injector::provide() const [with T = std::shared_ptr<int>]’
test.cpp:14:63: required from here
holders.hpp:48:85: error: ‘di::value_extractor<ContainerType, ValueType, std::shared_ptr<_Up> >::extract(const ContainerType<ValueType>&) [with ContainerType = std::shared_ptr; ValueType = int]’ is not a template [-fpermissive]
alue_extractor<ContainerType, ValueType, ReturnType>::template extract(value_);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
If I use the -fpermissive flag the code compiles and even works but obviously emits a warning.
So my question is: is it really me or this is gcc's error and if it's me writing nonconforming code then how should I fix it?
Thanks in advance.

template is not needed when calling extract, stupid mistake.
Thanks to #songyuanyao

Related

SFINAE overload on tuples of different sizes

Consider a struct that contains two types - FirstObjects and SecondObjects, which are both std::tuple<>, e.g. something like:
struct Objects
{
using FirstObjects = std::tuple<int, int>;
using SecondObjects = std::tuple<int>;
};
For convenience we add the following enum:
enum class ObjectCategory
{
FIRST,
SECOND
};
Now consider the following class that is templated on a type such as Objects as described above (the only contract is that it have FirstObjects and SecondObjects and that they be std::tuple):
template <typename T>
class SomeClass
{
public:
using FirstObjects = typename T::FirstObjects;
using SecondObjects = typename T::SecondObjects;
template <std::size_t Idx>
using FirstObject = typename std::tuple_element<Idx, FirstObjects>::type;
template <std::size_t Idx>
using SecondObject = typename std::tuple_element<Idx, SecondObjects>::type;
template <ObjectCategory Category, std::size_t Idx>
using ObjectType = std::conditional_t<Category == ObjectCategory::FIRST, FirstObject<Idx>, SecondObject<Idx>>;
template <ObjectCategory Category, std::size_t Idx>
std::enable_if_t<Category == ObjectCategory::FIRST, const ObjectType<Category, Idx>>& getObject()
{
return std::get<Idx>(firstObjects_);
}
template <ObjectCategory Category, std::size_t Idx>
std::enable_if_t<Category == ObjectCategory::SECOND, const ObjectType<Category, Idx>>& getObject()
{
return std::get<Idx>(secondObjects_);
}
template <ObjectCategory Category, std::size_t Idx>
void doSomething()
{
const ObjectType<Category, Idx>& obj = getObject<Category, Idx>();
}
private:
FirstObjects firstObjects_;
SecondObjects secondObjects_;
};
In brief, SomeClass hosts two member variables, firstObjects_ of type T::FirstObjects, and secondObjects_ of type T::SecondObjects, and has a doSomething() function which makes use of the SFINAE-overloaded getObject() getter method that returns the i-th object contained in firstObjects_ or in secondObjects_ depending on the ObjectCategory chosen.
The problem I have now is:
int main()
{
struct Objects
{
using FirstObjects = std::tuple<int, int>;
using SecondObjects = std::tuple<int>;
};
SomeClass<Objects> object{};
object.doSomething<ObjectCategory::FIRST, 0>(); // <------ works fine
object.doSomething<ObjectCategory::FIRST, 1>(); // <------ compiler error
}
The last line makes the compiler complain:
/usr/include/c++/4.9/tuple: In instantiation of 'struct std::tuple_element<1ul, std::tuple<int> >':
70:114: required by substitution of 'template<class T> template<ObjectCategory Category, long unsigned int Idx> using ObjectType = std::conditional_t<(Category == FIRST), typename std::tuple_element<Idx, typename T::FirstObjects>::type, typename std::tuple_element<Idx, typename T::SecondObjects>::type> [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]'
87:42: required from 'void SomeClass<T>::doSomething() [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]'
104:50: required from here
/usr/include/c++/4.9/tuple:682:12: error: invalid use of incomplete type 'struct std::tuple_element<0ul, std::tuple<> >'
struct tuple_element<__i, tuple<_Head, _Tail...> >
^
In file included from /usr/include/c++/4.9/tuple:38:0,
from 4:
/usr/include/c++/4.9/utility:85:11: error: declaration of 'struct std::tuple_element<0ul, std::tuple<> >'
class tuple_element;
^
In substitution of 'template<class T> template<ObjectCategory Category, long unsigned int Idx> using ObjectType = std::conditional_t<(Category == FIRST), typename std::tuple_element<Idx, typename T::FirstObjects>::type, typename std::tuple_element<Idx, typename T::SecondObjects>::type> [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]':
87:42: required from 'void SomeClass<T>::doSomething() [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]'
104:50: required from here
70:114: error: no type named 'type' in 'struct std::tuple_element<1ul, std::tuple<int> >'
It seems like it is not liking the fact that SecondObjects only has one element and therefore std::get<1>(secondObjects_) is not working? But why is this even happening in the first place since I'm calling doSomething on ObjectCategory::FIRST and not ObjectCategory::SECOND?
How can this be solved?
------ EDIT 1 ------
Solution pointed out by #Taekahn is to change the getObject() method as follows:
template <ObjectCategory Category, std::size_t Idx>
const auto& getObject()
{
return std::get<Idx>(firstObjects_);
}
template <ObjectCategory Category, std::size_t Idx, typename = std::enable_if_t<Category == ObjectCategory::SECOND>>
const auto& getObject()
{
return std::get<Idx>(secondObjects_);
}
It seems like the above works because the compiler does not evaluate / check the validity of the return type of a method that has been SFINAE'd out when one uses the keyword auto instead of the explicit type.
But this is only conjecture - would really appreciate a more knowledgeable C++ practitioner to weigh in here!
------ EDIT 2 ------
The above results in ambiguity when trying to do
object.doSomething<ObjectCategory::SECOND, 0>();
The WAR I've found so far is to modify the first getter function to
template <ObjectCategory Category, std::size_t Idx, typename = std::enable_if_t<Category == ObjectCategory::FIRST>, bool = 0 /* dummy param */>
auto& getObject()
{
return std::get<Idx>(firstObjects_);
}
i.e. to add the SFINAE as well as a dummy template arg at the end to allow overloading.

C++ variadic templates

I'm using a vector definition:
vector<lmlink_out> os{
lmlink_out(typeid(string &)),
lmlink_out(),
lmlink_out(typeid(int),typeid(char))
};
using the following code works (defined inside the class):
class lmlink_out {
vector<const type_info*> parameters;
...
public:
template<typename... T, std::size_t N = sizeof...(T)>
lmlink_out(const T&... args) : parameters({&args...}) {}
using this other leads to compilation error (defined outside the class):
class lmlink_out {
vector<const type_info*> parameters;
...
public:
template<typename... T, std::size_t N = sizeof...(T)>
lmlink_out(const T&... args);
};
template<typename... T, std::size_t N = sizeof...(T)>
lmlink_out::lmlink_out(const T&... args)
: parameters({(&args...})
{
}
the compiler (gcc version 10.2.1 20210110 (Debian 10.2.1-6)) returns:
error: no matching function for call to ‘std::vector<const
std::type_info*>::vector()’ 374 |
: parameters({(&args...}) note: candidate: ‘std::vector<_Tp,
_Alloc>::vector(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = const std::type_info*; _Alloc = std::allocator<const
std::type_info*>; std::vector<_Tp, _Alloc>::allocator_type =
std::allocator<const std::type_info*>]’ 625 |
vector(initializer_list<value_type> __l,
...
| ^~~~~~ /usr/include/c++/10/bits/stl_vector.h:625:43: note: no known conversion for argument 1 from ‘’ to ‘std::initializer_list<const std::type_info*>’
I think that two codes are semantically equivalent, why does the second give an error?
I need to add another constructor:
template<typename... T, std::size_t N = sizeof...(T)>
lmlink_out(const string &name, const T&... args) : name(name), parameters({&args...}) {}
but it lead to the same error above.
Your code has two simple problems:
You can't redefine template default arguments, i.e., you'll need to drop the = sizeof...(T) rom the definition of the ctor. It can only be present in the first declraation.
There is an excess '(' when calling the parameters ctor. It should be
: parameters({&args...})
This Compiler Explorer link shows the code compiling.

Qt/MinGW compiling error: no type named 'type' in 'struct std::enable_if<false, std::basic_ostream<char>&>'

I'm trying to compile a software by using Qt 5.12.10 with MinGW 7.3.0 64bit on Windows 10 64bit with multiple dependencies, after solving many errors of dependency directories i came to an error which i can't overcome until now. The gtest dependency seems to include ostream at which my Qt Creator stops with the following error message:
error: no type named 'type' in 'struct std::enable_if<false, std::basic_ostream<char>&>'
So i opened the file which is located in the MinGW folder and found the following codeblock which seems to be the problem:
#if __cplusplus >= 201103L
template<typename _Ch, typename _Up>
basic_ostream<_Ch, _Up>&
__is_convertible_to_basic_ostream_test(basic_ostream<_Ch, _Up>*);
template<typename _Tp, typename = void>
struct __is_convertible_to_basic_ostream_impl
{
using __ostream_type = void;
};
template<typename _Tp>
using __do_is_convertible_to_basic_ostream_impl =
decltype(__is_convertible_to_basic_ostream_test
(declval<typename remove_reference<_Tp>::type*>()));
template<typename _Tp>
struct __is_convertible_to_basic_ostream_impl
<_Tp,
__void_t<__do_is_convertible_to_basic_ostream_impl<_Tp>>>
{
using __ostream_type =
__do_is_convertible_to_basic_ostream_impl<_Tp>;
};
template<typename _Tp>
struct __is_convertible_to_basic_ostream
: __is_convertible_to_basic_ostream_impl<_Tp>
{
public:
using type = __not_<is_void<
typename __is_convertible_to_basic_ostream_impl<_Tp>::__ostream_type>>;
constexpr static bool value = type::value;
};
template<typename _Ostream, typename _Tp, typename = void>
struct __is_insertable : false_type {};
template<typename _Ostream, typename _Tp>
struct __is_insertable<_Ostream, _Tp,
__void_t<decltype(declval<_Ostream&>()
<< declval<const _Tp&>())>>
: true_type {};
template<typename _Ostream>
using __rvalue_ostream_type =
typename __is_convertible_to_basic_ostream<
_Ostream>::__ostream_type;
/**
* #brief Generic inserter for rvalue stream
* #param __os An input stream.
* #param __x A reference to the object being inserted.
* #return os
*
* This is just a forwarding function to allow insertion to
* rvalue streams since they won't bind to the inserter functions
* that take an lvalue reference.
*/
template<typename _Ostream, typename _Tp>
inline
typename enable_if<__and_<__not_<is_lvalue_reference<_Ostream>>,
__is_convertible_to_basic_ostream<_Ostream>,
__is_insertable<
__rvalue_ostream_type<_Ostream>,
const _Tp&>>::value,
__rvalue_ostream_type<_Ostream>>::type
operator<<(_Ostream&& __os, const _Tp& __x)
{
__rvalue_ostream_type<_Ostream> __ret_os = __os;
__ret_os << __x;
return __ret_os;
}
#endif // C++11
Especially this part seems to produce the error:
inline
typename enable_if<__and_<__not_<is_lvalue_reference<_Ostream>>,
__is_convertible_to_basic_ostream<_Ostream>,
__is_insertable<
__rvalue_ostream_type<_Ostream>,
const _Tp&>>::value,
__rvalue_ostream_type<_Ostream>>::type
operator<<(_Ostream&& __os, const _Tp& __x)
{
__rvalue_ostream_type<_Ostream> __ret_os = __os;
__ret_os << __x;
return __ret_os;
}
After some internet search i found that maybe something is wrong with the operator definition caused by the enable_if. But the code is not written by me and is part of the MinGW files, so i don't understand how to fix it. Can someone help?

Incomplete type `std::variant<...>` used in nested name specifier

I wrote the following code into a file named main.cpp.
It involves the curiously recurring template pattern (CRTP) with the standard type std::variant.
#include <string>
#include <variant>
#include <vector>
template<typename T>
struct either {
std::vector<T> arg;
};
template<typename T>
struct maybe_either: std::variant<T, either<maybe_either<T>>> {
template<typename U>
maybe_either(U&& v):
std::variant<T, either<maybe_either<T>>>(std::forward<U>(v)) {
}
};
struct var {
std::string name;
};
int main(int, char**) {
auto expression = maybe_either<var>(either<maybe_either<var>>{});
std::visit([&](auto&& v) {
using T = std::decay_t<decltype (v)>;
if constexpr (std::is_same_v<T, var>) {
// ...
} else if constexpr (std::is_same_v<T, either<maybe_either<var>>>) {
// ...
}
}, expression);
return 0;
}
When compiling it with the following command line, I get the error message below:
$ g++ -c -std=c++17 main.cpp
In file included from main.cpp:2:0:
/usr/include/c++/7/variant: In instantiation of ‘constexpr const size_t std::variant_size_v<maybe_either<var> >’:
/usr/include/c++/7/variant:702:10: required from ‘struct std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>’
/usr/include/c++/7/variant:1255:23: required from ‘constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = main(int, char**)::<lambda(auto:1&&)>; _Variants = {maybe_either<var>&}]’
main.cpp:32:18: required from here
/usr/include/c++/7/variant:97:29: error: incomplete type ‘std::variant_size<maybe_either<var> >’ used in nested name specifier
inline constexpr size_t variant_size_v = variant_size<_Variant>::value;
^~~~~~~~~~~~~~
/usr/include/c++/7/variant: In instantiation of ‘constexpr const auto std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>::_S_vtable’:
/usr/include/c++/7/variant:711:29: required from ‘struct std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>’
/usr/include/c++/7/variant:1255:23: required from ‘constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = main(int, char**)::<lambda(auto:1&&)>; _Variants = {maybe_either<var>&}]’
main.cpp:32:18: required from here
/usr/include/c++/7/variant:711:49: error: ‘_S_apply’ was not declared in this scope
static constexpr auto _S_vtable = _S_apply();
~~~~~~~~^~
My class maybe_either derived from std::variant<...> can be used normally in other contexts, but when I call std::visit(...) on it, it fails to compile. What is wrong?
This is basically LWG3052 which I'm trying to address in P2162.
maybe_either<T> isn't a specialization of std::variant - it inherits from one. And std::visit is currently underspecified. It's not at all clear what kinds of "variants" are allowed to be visited.
libstdc++ implements the original suggested direction in that library issue, which is only specializations of std::variant (of which you are not). libc++, on the other hand, allows types that inherit from std::variant to be visited, so it accepts your example.
The intent is that the example as-is will become well-formed eventually. But until then, you'll have to ensure that the visit you do is directly on a std::variant. You can do so by adding your own member or non-member visit that does this cast for you, so the callers don't have to do it themselves.
For example, this:
template<typename T>
struct maybe_either: std::variant<T, either<maybe_either<T>>> {
using base = typename maybe_either::variant;
template<typename U>
maybe_either(U&& v):
std::variant<T, either<maybe_either<T>>>(std::forward<U>(v)) {
}
template <typename F>
decltype(auto) visit(F&& f) & {
return std::visit(std::forward<F>(f), static_cast<base&>(*this));
}
};
allows this to work:
int main(int, char**) {
auto expression = maybe_either<var>(either<maybe_either<var>>{});
expression.visit([&](auto&& v) {
using T = std::decay_t<decltype (v)>;
if constexpr (std::is_same_v<T, var>) {
// ...
} else if constexpr (std::is_same_v<T, either<maybe_either<var>>>) {
// ...
}
});
return 0;
}

Test if Callable points at specific member function

I want to test if a _Callable __f is a (pointer to a) specific non-static member function memberfuncA.
I can test that with __f == &MyClass::memberfuncA, but only if I get no memberfuncB in the way, as in the example below, which results in a compiler error.
I tried to use std::is_convertible to make sure the cast will compile, but that didn't work; same with typeid().
#include <type_traits>
#include <typeinfo>
class A {};
class B {};
class MyClass {
public:
MyClass() {
A a;
B b;
push_exec(&MyClass::memberfuncA, this, a);
push_exec(&MyClass::memberfuncB, this, b);
}
template<typename _Callable, typename... _Args>
void push_exec(_Callable&& __f, _Args&&... __args)
{
// Test if typeid is the same
if(typeid(__f) == typeid(&MyClass::memberfuncA))
// Test if types are castable
if(std::is_convertible<typeof __f, typeof &MyClass::memberfuncA>::value)
// Test if __f is actually pointing to a specific member function (compiler error)
if(__f == &MyClass::memberfuncA)
return;
}
void memberfuncA(A a) { }
void memberfuncB(B b) { }
};
int main() {
MyClass mc;
}
Compiler output is
g++ --std=gnu++11 -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/callable_test.d" -MT"src/callable_test.o" -o "src/callable_test.o" "../src/callable_test.cpp"
../src/callable_test.cpp: In instantiation of ‘void MyClass::push_exec(_Callable&&, _Args&& ...) [with _Callable = void (MyClass::*)(B); _Args = {MyClass* const, B&}]’:
../src/callable_test.cpp:13:49: required from here
../src/callable_test.cpp:24:24: error: comparison between distinct pointer types ‘void (MyClass::*)(B)’ and ‘void (MyClass::*)(A)’ lacks a cast [-fpermissive]
if(__f == &MyClass::memberfuncA)
^
../src/callable_test.cpp:24:24: error: cannot convert ‘__f’ from type ‘void (MyClass::*)(B)’ to type ‘void MyClass::*’
../src/callable_test.cpp:24:24: error: cannot convert ‘&MyClass::memberfuncA’ from type ‘void (MyClass::*)(A)’ to type ‘void MyClass::*’
make: *** [src/callable_test.o] Error 1
Any idea how I can check if __f == &MyClass::memberfuncA?
If you really want compare __f and &MyClass::memberfunc, you can cast they to void *
if( ((void*)__f) == ((void*)&MyClass::memberfuncA) )
return;
Checking with std::is_convertible don't work because it's a compilation problem, not a run-time error. I mean: even if std::is_convertible value is false, the compiler try to compile the following if.
You could try std::is_convertible with SFINAE; something like this (EDIT: corrected version):
template<typename _Callable, typename... _Args>
typename std::enable_if<true == std::is_convertible<_Callable, void (MyClass::*)(A)>::value, void>::type push_exec(_Callable && __f, _Args && ... __args)
{
// do something
}
template<typename _Callable, typename... _Args>
typename std::enable_if<false == std::is_convertible<_Callable, void (MyClass::*)(A)>::value, void>::type push_exec(_Callable && __f, _Args && ... __args)
{
// do something else
}