enable_if fragment not working!!

Hi,


I have a short code sample using enable_if that does not want to compile.


Can somebody take a look at this?

I am exhausted and can't find the culprit!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
	struct have_sort { char c; };
	struct have_range { char c; have_sort c2; };
	struct have_nothing { char c; have_range c2; };

	template<typename T> have_sort test_sort(decltype(&T::sort), decltype(&T::sort));
	template<typename T> have_range test_sort(decltype(&T::begin), decltype(&T::end));
	template<typename T> have_nothing test_sort(...);


	template<typename T>
	typename std::enable_if<sizeof(test_sort<T>(nullptr,nullptr)) == sizeof(have_sort)>::type fast_sort(T& x)
	{
		x.sort();
	}

	template<typename T>
	typename std::enable_if<sizeof(test_sort<T>(nullptr, nullptr)) == sizeof(have_range)>::type fast_sort(T& x)
	{
		std::sort(x.begin(), x.end());
	}


The error output is:


error C2995: 'std::enable_if<0,void>::type fast_sort(T &)': function template has already been defined


Its probably a simple error!!

Regards,
Juan
Does this help?

I stuck with C++11
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <algorithm>
#include <list>
#include <vector>

template<typename T>
auto fast_sort_impl(T& x, int) -> decltype((void)x.sort())
{
	x.sort();
}

template<typename T>
auto fast_sort_impl(T& x...) -> decltype((void)x.begin(), (void)x.end())
{
	using std::sort;
	sort(x.begin(), x.end());
}

template <typename T> void fast_sort(T& x)
{
    return fast_sort_impl(x, 0); 
}

int main()
{
  std::list<int> x;
  std::vector<int> y;
  fast_sort(x);
  fast_sort(y);
}
Last edited on
It works and appreciate your input however the thing is I need to use enable_if and you are not addressing the problem: fast_sort is defined twice!! It shouldn't because enable_if should only allow the use one of the overloads -- but it is not instantiating anyone except enable_if(0, void)!!!
Last edited on
The thing is the 2 template functions are should provide overloads and each should compile for certain case but it seems to try to compile both!!


Its enable_if I have a problem with..

The signatures of fast_sort are the same that's why I get the "function template already defined"

So we need to have differing signatures for each fast_sort, right?
I need to use enable_if

Ok.

Your fast_sort compiles for types which
a. have no more than one overload of begin, end, or sort, and
b. have either a begin-end pair or sort, not both.
So types like std::vector<T> and std::list<T> are excluded, but a contrived type like
struct has_sort { void sort() {}; };
Is acceptable. This is what I tried to fix in the code I posted.

I don't know why MSVC won't compile your program. It looks like a compiler bug to me. However, given the error message, I find it possible that the program is ill-formed NDR and only MSVC is emitting a diagnostic.

So we need to have differing signatures for each fast_sort, right?

A function template's return type is part of its signature. [defns.signature.templ]
http://eel.is/c++draft/defns.signature.templ

Whether the code is well-formed seems to depend on [temp.over.link] or the ODR. Maybe MSVC's traditionally incorrect name lookup is involved too.
http://eel.is/c++draft/temp.over.link

It appears that you can sidestep the problem by comparing the sizes inside a class template as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <type_traits>
#include <utility>
#include <algorithm>

struct has_sort { void sort() {}; };
struct has_range { void begin() {}; void end() {}; };

struct have_sort { char c; };
struct have_range { char c; have_sort c2; };
struct have_nothing { char c; have_range c2; };

template<typename T> have_sort test_sort(decltype(&T::sort), decltype(&T::sort));
template<typename T> have_range test_sort(decltype(&T::begin), decltype(&T::end));
template<typename T> have_nothing test_sort(...);

template <std::size_t I, std::size_t J> struct eq { static constexpr bool value = (I == J); };

template<typename T>
typename std::enable_if<eq<sizeof(test_sort<T>(nullptr, nullptr)), sizeof(have_sort)>::value>::type fast_sort(T&)
{
}
template<typename T>
typename std::enable_if<eq<sizeof(test_sort<T>(nullptr, nullptr)), sizeof(have_range)>::value>::type fast_sort(T&)
{
    //std::sort(x.begin(), x.end());
}

int main()
{
    has_sort x;
    has_range y;
    fast_sort(x);
    fast_sort(y);
}

Which compiles under my version of cl.exe, although I'd not use this code without knowing for certain whether it is correct under Microsoft's toolchain.
D:\>cl /?
Microsoft (R) C/C++ Optimizing Compiler Version 19.27.29111 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

usage: cl [ option... ] filename... [ /link linkoption... ]


It could also be something dumb that I have forgotten. That's always a possibility.
Last edited on
So the question is whether the programs we're discussing violate the one definition rule in MSVC's interpretation.
Last edited on
Topic archived. No new replies allowed.