decltype in class template specialization - c++

I am trying to use decltype inside a template class as follows:
#include <functional>
template <typename T>
class A
{
typedef decltype(std::bind(&A::f, std::declval<A>())) some_type;
void f();
};
That works fine, but now I'd like to add an explicit specialization:
template <>
class A<void>
{
typedef decltype(std::bind(&A::f, std::declval<A>())) some_type;
void f();
};
This time g++ gives an error:
test.cpp:14:33: error: incomplete type 'A<void>' used in nested name specifier
What am I doing wrong? I am using gcc 4.5.
EDIT: If I move the declaration of void f(); to above the typedef, as suggested by Johannes, I get (slightly) different errors:
test.cpp:15:62: error: invalid use of incomplete type 'class A<void>'
test.cpp:13:1: error: declaration of 'class A<void>'
test.cpp:15:62: error: initializing argument 2 of 'std::_Bind<typename std::_Maybe_wrap_member_pointer<_Tp>::type(_ArgTypes ...)> std::bind(_Functor, _ArgTypes ...) [with _Functor = void (A<void>::*)(), _ArgTypes = {A<void>}, typename std::_Maybe_wrap_member_pointer<_Tp>::type = std::_Mem_fn<void (A<void>::*)()>]'
test.cpp:15:62: error: invalid use of incomplete type 'class A<void>'
test.cpp:13:1: error: declaration of 'class A<void>'
test.cpp:15:62: error: initializing argument 2 of 'std::_Bind<typename std::_Maybe_wrap_member_pointer<_Tp>::type(_ArgTypes ...)> std::bind(_Functor, _ArgTypes ...) [with _Functor = void (A<void>::*)(), _ArgTypes = {A<void>}, typename std::_Maybe_wrap_member_pointer<_Tp>::type = std::_Mem_fn<void (A<void>::*)()>]'

Your order is wrong. Try exchanging it
template <>
class A<void>
{
void f();
typedef decltype(std::bind(&A::f, std::declval<A>())) some_type;
};
In the primary template, the name A::f was dependent and the compiler delayed lookup to a point where f was declared (A::f is not really dependent in C++0x anymore, since A refers to the current instantiation and therefor f to a member of the current instantiation, but as there is a loophole in the current specification (it has to do with dependent base classes), the compiler delayed the lookup nontheless). In the explicit specialization, the name is not dependent and lookup is done immediately, which is the reason you need to declare f before referring to it.
Edit: You are wrongly using std::bind. The second argument you give is of type A<void>, which will be copied/moved by std::bind into its created call wrapper object. This requires a complete type A<void>.
If you want to merely pass a reference to A on which the member function is called, you can pass a declval<A*>(), which the std::bind mechanism equally detects as magical first argument to a member pointer invocation.
But it seems to me you want to look into std::function<>, instead of doing this std::bind and decltype mess. After all you have a powerful toolset given, but by using this doubtful decltype expression, you throw away all the genericity the Standard library gives you and restrict yourself to use of that single std::bind expression. That's no good.

std::bind requires A as a complete type (see answer by Johannes) and therefore you cannot use it at this point. As a workaround, if you encapsulate the some_type this will compile:
#include <functional>
template <typename T>
class A
{
void f();
struct some_type_helper
{
typedef decltype(std::bind(&A::f, std::declval<A>())) some_type;
};
};
template <>
class A<void>
{
void f();
struct some_type_helper;
};
struct A<void>::some_type_helper
{
typedef decltype(std::bind(&A::f, std::declval<A>())) some_type;
};

Related

Why does this SFINAE not work with enable_if when one conditional branch is inherited from the base class?

#include <bits/stdc++.h>
#include <type_traits>
// Type your code here, or load an example.
template <typename Types>
class C1 {
public:
using A=typename Types::A;
using B=typename Types::B;
template <typename Dummy = void>
inline typename std::enable_if<std::is_same<A, B>::value, Dummy>::type f() { }
};
template <typename Types>
class C2 : public C1<Types> {
public:
using A=typename Types::A;
using B=typename Types::B;
template <typename Dummy = void>
inline typename std::enable_if<!std::is_same<A, B>::value, Dummy>::type f() { }
};
template <typename Types>
class C3 : public C2<Types> {
public:
using A=typename Types::A;
using B=typename Types::B;
};
struct Types{
using A = int;
using B = int;
};
int main() {
C3<Types> c;
c.f();
return 0;
}
When I try to compile the above code when A and B are not same, I get the following error:
<source>: In function 'int main()':
<source>:42:9: error: no matching function for call to 'C3<Types>::f()'
42 | c.f();
| ^
<source>:23:77: note: candidate: 'template<class Dummy> typename std::enable_if<(! std::is_same<typename Types::A, typename Types::B>::value), Dummy>::type C2<Types>::f() [with Dummy = Dummy; Types = Types]'
23 | inline typename std::enable_if<!std::is_same<A, B>::value, Dummy>::type f() { }
| ^
<source>:23:77: note: template argument deduction/substitution failed:
<source>: In substitution of 'template<class Dummy> typename std::enable_if<false, Dummy>::type C2<Types>::f<Dummy>() [with Dummy = void]':
<source>:42:9: required from here
<source>:23:77: error: no type named 'type' in 'struct std::enable_if<false, void>'
Note that the code I have presented is not the exact code I use but a minimal reproducible example
EDIT: Put up a minimal reproducible example using godbolt in place of the earlier for a better understanding of the situation
As always with such problems, it falls down to the very definition of SFINAE. The S stands for "substitution", which happens into the template that we are trying to instantiate. That template is the member f, and not C.
Even though C is a template also, and both A and B are dependent types in C, they are not dependent type when f is instantiated. They are already known. As such, the condition std::is_same<A, B>::value is not value dependent on any template parameter of f. It doesn't depend on substation into f. This trips the following clause in the C++11 standard (taken from the last draft prior to publication):
[temp.res] (emphasis mine)
8 Knowing which names are type names allows the syntax of every
template definition to be checked. No diagnostic shall be issued for a
template definition for which a valid specialization can be generated.
If no valid specialization can be generated for a template definition, and that template is not instantiated, the template
definition is ill-formed, no diagnostic required.
This means that whatever Types is, if it doesn't uphold the condition of f, then the very definition of f (without even being instatiated), is already ill-formed whenever C is instantiated. A diagnostic is not required for this in general (since checking it is intractable in the general case), but compilers can diagnose it early often enough, and will tell you about the problem.
Now, as to how to fix it, just make the condition of f value dependent on its own template parameter. A simple re-write can be
template <bool Dummy = std::is_same<A, B>::value>
inline auto f(vector<int>& ctx, const string& r) ->
typename std::enable_if<Dummy>::type { }
Now the condition depends on substation in the correct context.
Of course, even if you fix the SFIANE problem, you still need to make sure the overload set is composed of the correct members. The f in C2 hides the f in C1. Add a using declaration to C2 so it's still a candidate
using C1<Types>::f;

May be a SFINAE BUG in complier when use template?

I want to use the standard code to write the utils like std::is_union,we know class type can not extends union type,it's error,so some code like these
#include <iostream>
template<typename T>
class class_type_can_extends :public T{
public:
using type = void;
};
template<typename T,typename U = void>
struct is_not_union:std::false_type {
};
template<typename T>
struct is_not_union < T, std::void_t<typename class_type_can_extends <T>::type >> :std::true_type {
};
class c_data{
};
union u_data{
};
int main(){
/*#1*/ std::cout<< is_not_union<c_data>::value<<std::endl; /*print true*/
/*#2*/ std::cout<< is_not_union<u_data>::value<<std::endl; /*this code make
all complier error*/
}
g++ print error:
main.cpp: In instantiation of ‘class class_type_can_extends<u_data>’:
main.cpp:26:43: recursively required by substitution of ‘template<class T> struct is_not_union<T, std::void_t<typename class_type_can_extends<T>::type> > [with T = u_data]’
main.cpp:26:43: required from here
main.cpp:3:7: error: base type ‘u_data’ fails to be a struct or class type
class class_type_can_extends :public T {
clang print error:
main.cpp:3:38: error: unions cannot be base classes
class class_type_can_extends :public T {
~~~~~~~^
main.cpp:14:47: note: in instantiation of template class 'class_type_can_extends<u_data>' requested here
struct is_not_union < T, std::void_t<typename class_type_can_extends <T>::type >> :std::true_type {
^
main.cpp:26:23: note: during template argument deduction for class template partial specialization 'is_not_union<T,
std::void_t<typename class_type_can_extends<T>::type> >' [with T = u_data]
/*#2*/ std::cout << is_not_union<u_data>::value << std::endl; /*this code make
^
main.cpp:26:23: note: in instantiation of template class 'is_not_union<u_data, void>' requested here
1 error generated.
vs:
error C2569
why #2 code make complier error,The complier would be using SFINAE rules on #2 code(substituted T by "u_data" ,then Failed ),and to chose primary template?why the sfinae not effective here,may be a bug here?
From cppreference:
Only the failures in the types and expressions in the immediate context of the function type or its template parameter types or its explicit specifier (since C++20) are SFINAE errors. If the evaluation of a substituted type/expression causes a side-effect such as instantiation of some template specialization, generation of an implicitly-defined member function, etc, errors in those side-effects are treated as hard errors
SFINAE applies on immediate context, here you have a hard error failure.
In typename class_type_can_extends<T>::type, SFINAE applies if type doesn't exist, not if instantiation of class_type_can_extends<T> fails.
Notice that we cannot distinguish between union and class types using only standard C++
(without std::is_union). Most compilers provide intrinsics for that.

Failure at disambiguating overloaded method

What follows is reduced from a system that stores pointers to methods along with their parameter's types. The user just provides type::method and the template machinery does the rest. When the method is overloaded the user must provide the signature of the desired method.
This was working very well until we tried it with some boost::asio stuff. The following code demonstrates the problem:
#include <boost/asio.hpp>
using namespace boost::asio::ip;
using namespace boost::system;
template <typename TT, typename MFP, MFP> struct OpM;
template <typename TR, typename TT, typename ... Ts, TR (TT::*f)(Ts...)>
struct OpM<TT, TR (TT::*)(Ts...), f> {};
using sig = error_code (tcp::socket::*)(const tcp::endpoint&, error_code&);
struct RM {
template <class C, typename R, typename ... Ps>
RM(R (C::*)(Ps...)) {
typedef OpM<C, R (C::*)(Ps...), &tcp::socket::connect> OP;
}
} MRegisterer(static_cast<sig>(&tcp::socket::connect));
g++ 8.3 fails to compile with the message:
g++ -std=c++17 -c connect.cpp
connect.cpp: In instantiation of 'RM::RM(R (C::*)(Ps ...)) [with C = boost::asio::basic_stream_socket<boost::asio::ip::tcp>; R = boost::system::error_code; Ps = {const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&, boost::system::error_code&}]':
connect.cpp:19:40: required from here
connect.cpp:17:46: error: conversion from 'boost::system::error_code (boost::asio::basic_socket<boost::asio::ip::tcp>::*)(const endpoint_type&, boost::system::error_code&)' {aka 'boost::system::error_code (boost::asio::basic_socket<boost::asio::ip::tcp>::*)(const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&, boost::system::error_code&)'} to 'boost::system::error_code (boost::asio::basic_stream_socket<boost::asio::ip::tcp>::*)(const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&, boost::system::error_code&)' in a converted constant expression
typedef OpM<C, R (C::*)(Ps...), &tcp::socket::connect> OP;
^~
connect.cpp:17:46: error: could not convert template argument '& boost::asio::basic_socket<boost::asio::ip::tcp>::connect' from '<unresolved overloaded function type>' to 'boost::system::error_code (boost::asio::basic_stream_socket<boost::asio::ip::tcp>::*)(const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&, boost::system::error_code&)'
It is weird that the error message refers to a conversion error from ... boost::asio::basic_socket ... to ... boost::asio::basic_stream_socket ... (and something similar for the endpoint parameter).
I'm providing the full type of the method, in RM seems to work fine but when the method is passed to OpM apparently the compiler gets confused.
What is wrong?
For completeness' sake, this is the output of clang++ 8.0:
~/bin/clang++ -std=c++17 -c connect.cpp
connect.cpp:17:37: error: conversion from '<overloaded function type>' to
'boost::system::error_code
(boost::asio::basic_stream_socket<boost::asio::ip::tcp>::*)(const
boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> &,
boost::system::error_code &)' is not allowed in a converted constant
expression
typedef OpM<C, R (C::*)(Ps...), &tcp::socket::connect> OP;
^~~~~~~~~~~~~~~~~~~~~
connect.cpp:19:3: note: in instantiation of function template specialization
'RM::RM<boost::asio::basic_stream_socket<boost::asio::ip::tcp>,
boost::system::error_code, const
boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> &,
boost::system::error_code &>' requested here
} MRegisterer(static_cast<sig>(&tcp::socket::connect));
^
1 error generated.
Here's a short reproduction of the same issue without Boost.Asio or even overloaded functions:
struct B {
void foo();
};
struct D : B { };
template <typename T, T> struct X { };
using T = X<void (D::*)(), &D::foo>; // error
The problem is, the type of &D::foo, despite being spelled D::, is actually void (B::*)(). And that type is not implicitly convertible to void (D::*)().
The nice thing for you is that since you're using C++17, you don't actually have to go through this explicit typing rigamarole, you can just write:
template <auto F> struct X { };
using T = X<&D::foo>; // fine
Or rework the whole thing to use pointers to function instead, and turn your pointer-to-member function into a function taking a D* (which you can do with a lambda or write out a function template and use an explicit specialization of it).

C++ Concepts TS: Require nested templated type alias

For a generic library I'm trying to define a concept in terms of having a correct implementation of a traits struct. In particular I want to check that the user has provided all required nested types, static member functions and data members. However, I can't find a way to require a nested templated type(-alias).
I have a declaration of the traits struct
template <typename>
struct trait;
and a specialization for chars
template <>
struct trait<char> {
template <typename>
using type = int;
};
I now define my concept in terms of this trait
template <typename T>
concept bool SatisfiesTrait = requires() {
typename trait<T>; // require the user to have spcialized
// for their type
typename trait<T>::type<long long>; // require the nested template
};
as well as a function requiring a type satisfying this concept
constexpr bool foo(SatisfiesTrait) { return true; }
In my main method I then try to call this function with a char:
int main() {
foo('c');
}
When compiling all this with GCC I get the error message
prog.cc:15:24: error: 'trait<T>::type' is not a type
typename trait<T>::type<long long>;
^~~~
prog.cc: In function 'int main()':
prog.cc:26:11: error: cannot call function 'constexpr bool foo(auto:1) [with auto:1 = char]'
foo('c');
^
prog.cc:18:16: note: constraints not satisfied
constexpr bool foo(SatisfiesTrait) {
^~~
prog.cc:18:16: note: in the expansion of concept 'SatisfiesTrait<auto:1>' template<class T> concept const bool SatisfiesTrait<T> [with T = char]
However, when I change my main function to
int main() {
typename trait<char>::type<long long> v;
(void) v;
foo('c');
}
and comment out the requirement of the nested alias template it compiles just fine. The same problem occurs when the nested type has a non-type template parameter instead of a type parameter.
Am I doing something wrong here or is this a bug in GCCs implementation of the Concepts TS?
The code can also be found on Wandbox.

pre-typedef'ing a variadic-function-pointer argument

I have a function foo that takes a variadic function pointer as its argument.
I would like to use "using" to define the argument's type prior to the function declaration.
template <typename ... vARGS>
using TFuncType = void(*)(vARGS ... V_args);
template <typename ... vARGS>
void foo(TFuncType<vARGS ...> funcptr) {}
void bar(int i) {}
int main() {
foo(&bar); // This line fails to compile.
}
This doesn't compile. The error (via clang using c++1z) is:
/make/proj/test/variadic-funcparam-deduce2.cpp:39:5: error: no matching function for call to 'foo'
foo(&bar);
^~~
/make/proj/test/variadic-funcparam-deduce2.cpp:33:36: note: candidate template ignored: substitution failure [with vARGS = int]
template <typename ... vARGS> void foo(TFuncType<vARGS ...> funcptr) {}
Why is the "int" substitution failing?
I can successfully compile if I explicitly write the type inside foo():
template <typename ... vARGS>
void foo(void(*funcptr)(vARGS ... V_args)) {}
But I cannot get the initial ("using") version to work even when explicitly specifying the template parameters, and using a pre-casted TFuncType<int> for the argument, i.e.:
int main() {
TF_call<int> fptr = &bar; // This line is OK.
foo<int>(fptr);
}
Does anyone know what's up here?
Is there something strange about using typedef'd ("using") variadics and/or function pointers that I'm missing?
I believe this may be related to the following text which I copied from this answer that itself takes from the C++ standard in 14.5.7 [temp.alias] paragraph 2:
When a template-id refers to the specialization of an alias template,
it is equivalent to the associated type obtained by substitution of
its template-arguments for the template-parameters in the type-id of
the alias template. [ Note: An alias template name is never deduced. —
end note ]
If I'm interpreting that right, it means that GCC accepting the code is actually non-conforming.