Class influences other class behavior in Visual Studio 2015 - c++

I have the following code to detect if a type T has a function with a specific return type and parameters parameters (simplified version generated by some more complex macros):
#include <vector>
#include <complex>
#include <type_traits>
namespace has_private
{
template <typename...>
using void_t = void;
template <typename T>
struct identity
{
using type = T;
};
template <typename T>
struct add_const
{
using type = const T;
};
} // namespace has_private
template <typename T, typename = void>
struct has_bytes : std::false_type
{
static_assert(sizeof(T) > 0, "Incomplete type");
};
template <typename T>
struct has_bytes<
T,
has_private::void_t<decltype(std::declval<typename has_private::add_const<T&>::type>().bytes())>>
: std::is_same<decltype(std::declval<typename has_private::add_const<T&>::type>().bytes()), size_t>
{
};
template <typename T, typename = void>
struct has_capacity : std::false_type
{
static_assert(sizeof(T) > 0, "Incomplete type");
};
template <typename T>
struct has_capacity<
T,
has_private::void_t<decltype(std::declval<typename has_private::add_const<T&>::type>().capacity())>>
: std::is_same<decltype(std::declval<typename has_private::add_const<T&>::type>().capacity()), size_t>
{
};
static_assert(has_capacity<std::vector<int>>::value, "should have");
static_assert(has_capacity<std::complex<int>>::value == false, "shouldn't have");
This code compiles fine with VS2019, GCC 7+, clang 7+ but with VS2015 it doesn't compile. If I remove the has_bytes or if I move has_bytes after has_capacity the code compiles with VS2015. By the looks of it this seems to be a compiler bug.
Is this a compiler bug? If yes, do you know any workarounds this bug?
I can't just move the has_bytes after has_capacity because these trait classes are generated using macros from multiple places and their behavior must be reliable.
EDIT:
This is the complete error message:
1>------ Build started: Project: ConsoleApplication1, Configuration: Debug Win32 ------
1>ConsoleApplication1.cpp
1>source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(51): error C2338: should have
1>Done building project "ConsoleApplication1.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
EDIT: I'm using latest version of VS2019 with Visual Studio 2015 (v140) Toolchain. I'm not exactly sure how to check the version in this case

Related

Compilation failing in Visual Studio with template template parameter using non-type parameter pack

In Visual Studio I get this compiler error with the below code which I think should compile fine (gcc 11.2 and clang 14.0 compile it). The non-type parameter pack (auto...) means I should be able to pass in a template with any number of non-type template parameters, but the compilation only succeeds when passing in a template that uses a single non-type template parameter.
Is there something I'm missing that will make this work in Visual Studio?
I'm using "/std:c++latest" on Visual Studio 2019, version 16.11.12
error C3201: the template parameter list for class template 'testValues::twoParams' does not match the template parameter list for template parameter 'X'
namespace testValues {
template<template<auto...> class X>
struct templatetemplateparam {};
template<int i>
struct oneParam {};
using oneParam_t = templatetemplateparam<oneParam>;
template<int i, int j>
struct twoParams {};
using twoParams_t = templatetemplateparam<twoParams>;
}
The equivalent code with a specific non-type parameter or a type parameter pack does compile, if that's relevant:
namespace testIntValues {
template<template<int...> class X>
struct templatetemplateparam {};
template<int i>
struct oneParam {};
using oneParam_t = templatetemplateparam<oneParam>;
template<int i, int j>
struct twoParams {};
using twoParams_t = templatetemplateparam<twoParams>;
}
namespace testTypes {
template<template<typename...> class X>
struct templatetemplateparam {};
template<typename>
struct oneParam {};
using oneParam_t = templatetemplateparam<oneParam>;
template<typename, typename>
struct twoParams {};
using twoParams_t = templatetemplateparam<twoParams>;
}
I've reported this to MS at it looks like a bug in Visual Studio to me: https://developercommunity.visualstudio.com/t/Compilation-error-C3021-when-using-value/10015492

Undefined type when using std::enable_if and sizeof. (visual studio 2017)

I get what appears to be a Visual Studio bug when I compile time test if a type with a default template parameter has some property. In the minimal example below I use std::is_integer.
When compiling the following program with Visual Studio 2017
#include <type_traits>
#include <utility>
#include <algorithm>
template<typename U,
typename Enabled = typename std::enable_if<std::is_integral<U>::value>::type>
struct wrapper
{
};
template<typename U, typename storage_type>
using is_small = std::is_void<typename std::enable_if <
(sizeof(wrapper<U, char>) <= sizeof(storage_type))
>::type>;
I get the following output
1>bug.cpp(13): error C2027: use of undefined type 'wrapper<U,char>'
1>bug.cpp(13): note: see declaration of 'wrapper<U,char>'
The same program does compile on g++ 6.1.
The program does compile on Visual Studio when the default parameter Enable is removed. In addition, when I perform the same sizeof(...)<=sizeof(...) test in the following template member function the program compiles fine (with is_small removed).
struct c
{
template<typename U, typename storage_type>
typename std::enable_if<(sizeof(wrapper<U, char>) <= sizeof(storage_type))>::value
foo(U u, storage_type t)
{}
};
Somehow the issue is related to the definition of is_small.
Does anyone know what the issue is? Is it a Visual Studio bug? Is there a workaround?
Edit
Reduced version:
template<class, class> struct A { };
template<int> struct B { };
template<class T> constexpr auto x = sizeof(A<T, int>); // works
template<class T> struct C : B<sizeof(A<T, int>)> { }; // works
template<class T> using D = B<sizeof(A<T, int>)>; // doesn't work
A possible workaround could be to use x instead of is_small.
Bug report submitted: https://developercommunity.visualstudio.com/content/problem/204504/issues-with-sizeof-alias-templates-and-virtual-fun.html

Variadic template pack expansion fails with VS2017 RC but not VS2015

The test program compiles with VS2015 but not with VS2017 RC. Below is the error I am getting while compiling the test program with VS2017 RC:
/* main.cpp */
#include <type_traits>
template <typename T>
struct A_impl
{
using type = std::false_type;
};
template <typename T>
using A = typename A_impl<T>::type;
template <bool... B>
struct logic_helper;
template <bool... B>
using none_t = std::is_same<logic_helper<B...>, logic_helper<(B && false)...>>;
template <typename... C>
struct Foo
{
// Compile error:
// error C3520: 'C': parameter pack must be expanded in this context
using FooType = none_t<A<C>::value...>;
};
int main()
{
Foo<int, int, int> foo;
return 0;
}
I would like to know why the newest Visual Studio fails to compile the code while the older version has no problem with it. If possible, is there a solution to this compile error for VS2017 RC?
I found a workaround to the problem, but I still can't understand why the compiler complains with my first approach:
template <typename... C>
struct Foo
{
// Workaround
using FooType2 = logic_helper<A<C>::value...>;
using FooType3 = logic_helper<(A<C>::value && false)...>;
using FooType4 = std::is_same<FooType2, FooType3>;
};

Sequential custom type traits behaving as one

I'm trying to define custom type traits (to which i'm relatively new), that check if given type supports a specific operation. If i just have one type trait it works as intended, but if i put two type traits sequentially both of them behave as one (when i use second one, it returns same result as the first one).
I'm using Visual Studio 2015 (v140).
Code sample:
#include <type_traits>
template<typename T, typename = void>
struct is_comparable_by_equal_to
: std::false_type {};
template<typename T>
struct is_comparable_by_equal_to<T, typename std::enable_if<true,
decltype(std::declval<T>() == std::declval<T>(), (void)0)>::type>
: std::true_type {};
template<typename T, typename = void>
struct is_comparable_by_not_equal_to
: std::false_type {};
template<typename T>
struct is_comparable_by_not_equal_to<T, typename std::enable_if<true,
decltype(std::declval<T>() != std::declval<T>(), (void)0)>::type>
: std::true_type {};
class mClass {
public:
bool operator== (const mClass& obj) {
return false;
}
};
int main() {
printf("%s: %d\n", "is_comparable_by_equal_to",
is_comparable_by_equal_to<mClass>::value);
printf("%s: %d\n", "is_comparable_by_not_equal_to",
is_comparable_by_not_equal_to<mClass>::value);
}
Resulting output:
is_comparable_by_equal_to: 1
is_comparable_by_not_equal_to: 1
Intended result output:
is_comparable_by_equal_to: 1
is_comparable_by_not_equal_to: 0
Question is why and how to solve the problem? Also i'm open for improvements how to define such custom type traits.
I compiled the code with g++ -std=c++11 and it worked as intended.
As it turned out, problem was in Visual Studio 2015 compiler version (VS2015 update 2), when I updated to latest version (update 3) it worked as intended.

VS2015: Variadic template specialization

This code
#include <iostream>
#include <type_traits>
template<typename Head, typename... Tail>
struct Is_Admitted {
constexpr static bool value = Is_Admitted<Head>::value && Is_Admitted<Tail...>::value;
};
template<>
template<typename T>
struct Is_Admitted<T> : public std::false_type{};
template<> struct Is_Admitted<int> : public std::true_type{};
template<> struct Is_Admitted<double> : public std::true_type{};
int main()
{
std::cout << Is_Admitted<int, double>::value << '\n';
std::cout << Is_Admitted<int, char>::value << '\n';
}
compiles and run fine under GCC (>=4.7, with c++11, c++14 or c++17 support enabled)
compiles with a warning and run fine with clang 3.6 (with c++11, c++14 or c++17 support enabled)
does not compile under VS2015RC (or Preview) with the following errors:
(error descriptions are translated in English by myself as I was not able to set English as compiler language, so they might mismatch with original ones)
error C2910: 'Is_Admitted<T,>': impossible to perform explicit specialization
error C2065: 'value': undeclared identifier
error C2976: 'Is_Admitted': insufficients template arguments
error C2131: constant expression does not return any value
Which compiler is right and which one is wrong? Is that code compliant to either c++11, c++14 or c++17 standard?
And what is the right way to do what I'm trying to do, that is a variadic type function that returns true only if all template type parameters are of some admitted types?
You have an extra template<> here:
template<> // <===
template<typename T>
struct Is_Admitted<T> : public std::false_type{};
Your code gives me the same error via webcompiler.
Simply remove it and it compiles fine. I do not understand how this compiles on either gcc or clang.
Two template declarations are only necessary when you're defining a member template of a class template outside of the class definition, e.g.:
template <typename T>
struct Foo {
template <typename U>
void bar();
};
template <typename T>
template <typename U>
void Foo<T>::bar() { ... }