Sunday, April 7, 2019

Digest for comp.lang.c++@googlegroups.com - 16 updates in 5 topics

Daniel <danielaparker@gmail.com>: Apr 06 05:00PM -0700

Consider the function
 
template <class J>
J f()
{
return J();
}
 
I'd like to have two implementations of f. One when J is an instantiation of the template class
 
template <class T>
struct A
{
typedef T t_type;
typedef typename T::char_type char_type; // (*)
};
 
And a second when J is any other class that has a default constructor.
 
I tried the following:
 
template <class T,class Enable=void>
struct is_A : std::false_type
{};
 
template <class T>
struct is_A<T, decltype(std::declval<A<typename T::t_type>>(),void())> : std::true_type
{};
 
template <class J>
typename std::enable_if<is_A<J>::value,J>::type
f()
{
std::cout << "J is a specialization of A\n";
return J();
}
 
template <class J>
typename std::enable_if<!is_A<J>::value, J>::type
f()
{
std::cout << "J is not a specialization of A\n";
return J();
}
 
But when attempting to construct
 
struct B
{
typedef char char_type;
};
 
auto j1 = f<A<B>>(); // (1)
auto j2 = f<std::vector<int>>(); // (2)
 
(2) fails to compile because of line (*) in the definition of A. So this
approach doesn't work.
 
Any suggestions?
 
Thanks,
Daniel
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Apr 07 05:44AM +0200

On 07.04.2019 02:00, Daniel wrote:
 
> Any suggestions?
 
> Thanks,
> Daniel
 
Your code, with necessary #include statements added as shown below,
works with MingW g++ 8.2.0 and Visual C++ 2019,
 
Try to create a small but complete example that reproduces the undesired
behavior.
 
When/if you ask here about it, if it still doesn't work, consider ALL
the points in the FAQ item "How do I post a question about code that
doesn't work correctly?", available at e.g. <url:
http://www.dietmar-kuehl.de/mirror/c++-faq/how-to-post.html#faq-5.8>.
 
 
-------------------------------------------------------------------------
#include <type_traits> // std::false_type
 
// template <class J>
// J f()
// {
// return J();
// }
 
template <class T>
struct A
{
typedef T t_type;
typedef typename T::char_type char_type; // (*)
};
 
template <class T,class Enable=void>
struct is_A : std::false_type
{};
 
template <class T>
struct is_A<T, decltype(std::declval<A<typename T::t_type>>(),void())> :
std::true_type
{};
 
 
#include <iostream>
#include <vector>
 
template <class J>
typename std::enable_if<is_A<J>::value,J>::type
f()
{
std::cout << "J is a specialization of A\n";
return J();
}
 
template <class J>
typename std::enable_if<!is_A<J>::value, J>::type
f()
{
std::cout << "J is not a specialization of A\n";
return J();
}
 
struct B
{
typedef char char_type;
};
 
auto main() -> int
{
auto j1 = f<A<B>>(); // (1)
auto j2 = f<std::vector<int>>(); // (2)
(void)j1, (void)j2;
}
-------------------------------------------------------------------------
 
 
Results:
 
 
J is a specialization of A
J is not a specialization of A
 
 
The code can IMO be expressed more cleanly, but that's a different matter.
 
 
Cheers!,
 
- Alf
Sam <sam@email-scan.com>: Apr 07 08:41AM -0400

Daniel writes:
 
> typedef typename T::char_type char_type; // (*)
> };
 
> And a second when J is any other class that has a default constructor.
 
It's much simpler to avoid SFINAE with an inline wrapper that uses partial
specialization of a template type. I expect any modern C++ compiler to
optimize away the wrapper call to nothing.
 
#include <iostream>
 
template <class T>
struct A
{
typedef T t_type;
};
 
template <class J>
struct generic_f {
 
static J f()
{
std::cout << "Generic" << std::endl;
return J();
}
};
 
template<typename J>
struct generic_f<A<J>> {
 
static A<J> f()
{
std::cout << "Specialized" << std::endl;
return A<J>();
}
};
 
template <class T>
inline T f()
{
 
return generic_f<T>::f();
}
 
int main()
{
f<int>();
f<A<int>>();
return 0;
}
Daniel <danielaparker@gmail.com>: Apr 07 07:48AM -0700

On Saturday, April 6, 2019 at 11:44:34 PM UTC-4, Alf P. Steinbach wrote:
 
> Your code, with necessary #include statements added as shown below,
> works with MingW g++ 8.2.0 and Visual C++ 2019,
 
But not with Visual C++ 140 (2015) or 141 (vs 2017). Given that, I had
assumed something was wrong with my understanding of SFINAE with decltype
and declval, but I checked and I see that it does work with GCC 4.8 and
later on linux, and clang 3.9 and later on linux, so I guess this is an
earlier Visual C++ bug. But nonetheless, a showstopper, the code having to
work in all these environments.
 
I don't suppose you would have any suggested alternatives that would work
with Visual C++ 140 (2015) or 141 (vs 2017)?
 
Thanks,
Daniel
Daniel <danielaparker@gmail.com>: Apr 07 07:54AM -0700

On Sunday, April 7, 2019 at 8:41:50 AM UTC-4, Sam wrote:
 
> It's much simpler to avoid SFINAE with an inline wrapper that uses partial
> specialization of a template type. I expect any modern C++ compiler to
> optimize away the wrapper call to nothing.
 
Thanks, I may end up doing it that way.
 
Daniel
Daniel <danielaparker@gmail.com>: Apr 07 12:33PM -0700

On Sunday, April 7, 2019 at 10:49:00 AM UTC-4, Daniel wrote:
 
> > Your code, with necessary #include statements added as shown below,
> > works with MingW g++ 8.2.0 and Visual C++ 2019,
 
> But not with Visual C++ 140 (2015) or 141 (vs 2017).
 
I'm currently experimenting with code similar to that below. Any additional
comments appreciated.
 
Thanks,
Daniel
 
#include <type_traits>
#include <iostream>
#include <vector>
 
template <class T>
struct A
{
typedef T t_type;
typedef typename T::char_type char_type; // (*)
};
 
template <class T,class Enable=void>
struct is_A : std::false_type
{};
 
#if defined(_MSC_VER) && _MSC_VER < 1916
template <class T>
struct is_A<T, typename std::enable_if<!std::is_void<typename T::t_type>::value && !std::is_void<typename T::char_type>::value>::type> :
std::true_type
{};
#else
template <class T>
struct is_A<T, decltype(std::declval<A<typename T::t_type>>(),void())> :
std::true_type
{};

No comments: