Rules for operator overload resolution in namespace - c++11

While implementing detection of presence of equality operators i noticed that for some reasons code below fail to compile on GCC v4, ..., v7 but seems to compile fine in Clang. The exact error (below) is due to inability of GCC to find overloaded operator templated inside the namespace:
stackoverflow-operator.cpp:15:51: error: no match for 'operator==' (operand types are 'A' and 'A')
std::is_same<bool, decltype(std::declval<T>() == std::declval<T>())>::value and
stackoverflow-operator.cpp:16:51: error: no match for 'operator!=' (operand types are 'A' and 'A')
std::is_same<bool, decltype(std::declval<T>() != std::declval<T> ())>::value
Interestingly using the same operator inside the function foo is fine in both GCC and Clang.
My question is: which compiler is right here? And if that is GCC then why it is right not to find operator overloads in cases like this?
#include <type_traits>
#include <iostream>
struct A {};
namespace has_equal_operator_impl {
struct NoEqualOperator {};
template<typename T1, typename T2> NoEqualOperator operator== (T1, T2) { return NoEqualOperator(); }
template<typename T1, typename T2> NoEqualOperator operator!= (T1, T2) { return NoEqualOperator(); }
template<typename T>
typename std::enable_if<
std::is_same<bool, decltype(std::declval<T>() == std::declval<T>())>::value and
std::is_same<bool, decltype(std::declval<T>() != std::declval<T>())>::value
, bool>::type
constexpr has_equal_operator()
{
return true;
}
template<typename T>
typename std::enable_if<
not (std::is_same<bool, decltype(std::declval<T>() == std::declval<T>())>::value and
std::is_same<bool, decltype(std::declval<T>() != std::declval<T>())>::value )
, bool>::type
constexpr has_equal_operator()
{
return false;
}
void foo()
{
auto r = A() == A(); // somehow this works just fine in GCC
}
}
int main()
{
// Only work in Clang and does not work in GCC because local namespace operators is not consider
std::cout << "has_equal_operator<int>() --> " << has_equal_operator_impl::has_equal_operator<int>() << std::endl;
std::cout << "has_equal_operator<A>() --> " << has_equal_operator_impl::has_equal_operator<A>() << std::endl;
return 0;
}

Related

Using boost::program_options with std::optional

Boost's program_options library now supports boost::optional, can the same be done with std::optional?
I attempted to modify both the documentation example and the code in the PR, but neither seems to work.
For example, the very simple case for integers (before trying template specializations):
void validate(boost::any& v, const std::vector<std::string>& values, std::optional<int>* target_type,
int) {
using namespace boost::program_options;
validators::check_first_occurrence(v);
const string& s = validators::get_single_string(values);
int n = lexical_cast<int>(s);
v = any(std::make_optional<int>(n));
}
fails with the error that the target type is not istreamable:
external/boost/boost/lexical_cast/detail/converter_lexical.hpp:243:13:
error: static_assert failed due to requirement
'has_right_shift<std::__1::basic_istream<char>, std::__1::optional<int>, boost::binary_op_detail::dont_care>::value || boost::has_right_shift<std::__1::basic_istream<wchar_t>, std::__1::optional<int>, boost::binary_op_detail::dont_care>::value'
"Target type is neither std::istream`able nor std::wistream`able"
The problem with things like validate (and operator>> as well) is often ADL¹.
You need to declare the overload in one of the associated namespaces. In this case, because int is a primitive type, the only associated namespaces come from library code:
std for optional, vector, string, allocator, char_traits (yes these all count!)
boost for any
You'd prefer not to add your code in those namespaces, because you might interfere with library functions or invite future breakage when the library implementation details change.
If you had to choose, you'd prefer to choose boost here, because
that's the library providing the feature at hand
the validate free function is explicitly designed to be an customization point
Sidenote: Keep an eye out for tag_invoke - a better way to build customization points in libraries
The Fix
After all this verbiage, the solution is very simple:
namespace boost {
void validate(boost::any& v, const std::vector<std::string>& values,
std::optional<int>*, int) {
using namespace boost::program_options;
validators::check_first_occurrence(v);
const std::string& s = validators::get_single_string(values);
int n = boost::lexical_cast<int>(s);
v = boost::any(std::make_optional<int>(n));
}
} // namespace boost
Adding two lines made it work: Live On Wandbox.
Other Notes:
The "solution" injecting operator>> in general is less pure
because
it has a potential to "infect" all other code with ADL-visible overloads that might interfere. Way more code uses operator>> than
boost's validate function
it thereby invites UB due to
ODR violations,
when another translation unit, potentially legitimely, defines
another operator>> for the same arguments.
On recent compilers you can say vm.contains instead of the slightly abusive vm.count
There's another snag with non-streamable types, where, if you define a default value, you probably also need to specify the string representation with it.
Listing
Compiling on Compiler Explorer
#include <boost/program_options.hpp>
#include <optional>
#include <iostream>
namespace po = boost::program_options;
namespace boost {
void validate(boost::any& v, const std::vector<std::string>& values,
std::optional<int>*, int) {
using namespace boost::program_options;
validators::check_first_occurrence(v);
const std::string& s = validators::get_single_string(values);
int n = boost::lexical_cast<int>(s);
v = boost::any(std::make_optional<int>(n));
}
} // namespace boost
int main(int ac, char* av[]) {
try {
using Value = std::optional<int>;
po::options_description desc("Allowed options");
desc.add_options()
("help", "produce help message")
("value", po::value<Value>()->default_value(10, "10"),
"value")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
if (vm.contains("value")) {
std::cout << "value is " << vm["value"].as<Value>().value() << "\n";
}
} catch (std::exception& e) {
std::cout << e.what() << "\n";
return 1;
}
}
BONUS
As an added exercise, let's demonstrate that if your optional value_type is not a primitive, but rather your library type, declared in a namespace MyLib, then we don't have most of the trade-offs above:
namespace MyLib {
template <typename T> struct MyValue {
MyValue(T v = {}) : value(std::move(v)) {}
private:
T value;
friend std::istream& operator>>(std::istream& is, MyValue& mv) {
return is >> mv.value;
}
friend std::ostream& operator<<(std::ostream& os, MyValue const& mv) {
return os << mv.value;
}
};
Now you could provide generic validators for any types in your MyLib namespace, be it optional or not, and have ADL find them through your MyLib namespace:
template <typename T, typename Values>
void validate(boost::any& v, Values const& values, T*, int) {
po::validators::check_first_occurrence(v);
v = boost::lexical_cast<T>(
po::validators::get_single_string(values));
}
template <typename T, typename Values>
void validate(boost::any& v, Values const& values, std::optional<T>*, int) {
po::validators::check_first_occurrence(v);
v = std::make_optional(
boost::lexical_cast<T>(
po::validators::get_single_string(values)));
}
} // namespace MyLib
See Live Demo
#include <boost/program_options.hpp>
#include <iostream>
#include <iomanip>
namespace po = boost::program_options;
namespace MyLib {
template <typename T> struct MyValue {
MyValue(T v = {}) : value(std::move(v)) {}
private:
T value;
friend std::istream& operator>>(std::istream& is, MyValue& mv) {
return is >> std::boolalpha >> mv.value;
}
friend std::ostream& operator<<(std::ostream& os, MyValue const& mv) {
return os << std::boolalpha << mv.value;
}
};
// Provide generic validators for any types in your MyLib namespace, be it
// optional or not
template <typename T, typename Values>
void validate(boost::any& v, Values const& values, T*, int) {
po::validators::check_first_occurrence(v);
v = boost::lexical_cast<T>(
po::validators::get_single_string(values));
}
template <typename T, typename Values>
void validate(boost::any& v, Values const& values, std::optional<T>*, int) {
po::validators::check_first_occurrence(v);
v = std::make_optional(
boost::lexical_cast<T>(
po::validators::get_single_string(values)));
}
} // namespace MyLib
int main(int ac, char* av[]) {
try {
using Int = MyLib::MyValue<int>;
using OptInt = std::optional<MyLib::MyValue<int>>;
using OptStr = std::optional<MyLib::MyValue<std::string> >;
po::options_description desc("Allowed options");
desc.add_options()
("ival", po::value<Int>()->default_value(Int{10}),
"integer value")
("opti", po::value<OptInt>()->default_value(OptInt{}, "(nullopt)"),
"optional integer value")
("sval", po::value<OptStr>()->default_value(OptStr{"secret"}, "'secret'"),
"optional string value")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
std::cout << "Options: " << desc << "\n";
if (vm.contains("ival")) {
std::cout << "ival is " << vm["ival"].as<Int>() << "\n";
}
if (vm.contains("opti")) {
if (auto& v = vm["opti"].as<OptInt>())
std::cout << "opti is " << v.value() << "\n";
else
std::cout << "opti is nullopt\n";
}
if (vm.contains("sval")) {
if (auto& v = vm["sval"].as<OptStr>())
std::cout << "sval is " << v.value() << "\n";
else
std::cout << "sval is nullopt\n";
}
} catch (std::exception& e) {
std::cout << e.what() << "\n";
return 1;
}
}
For ./a.out --ival=42 --sval=LtUaE prints:
Options: Allowed options:
--ival arg (=10) integer value
--opti arg (=(nullopt)) optional integer value
--sval arg (='secret') optional string value
ival is 42
opti is nullopt
sval is LtUaE
¹ see also See also Why Does Boost Use a Global Function Override to Implement Custom Validators in "Program Options"

define a common template c++ class for arithmetic types and pointers

I need to define a common template class for arithmetic types and pointer types.
following is the code I tried but I never got it correct. I need to implement it using g++4.4.7, because of that I am using boost.
The output should be ARITHMETIC followed by POINTER.
//primary template class
template <class T, class Enable = void>
struct Class
{
};
template <class C>
struct Class<C, typename boost::enable_if_c<boost::is_arithmetic<C>::value || boost::is_pointer<C>::value>::type>
{
static inline typename boost::enable_if_c<boost::is_arithmetic<C>::value, void>::type
print(const C& obj)
{
std::cout << "ARITHMETIC TYPE" << std::endl;
}
static inline typename boost::enable_if_c<boost::is_pointer<C>::value, void>::type
print(const C& obj)
{
Class<uint64_t>::print(reinterpret_cast<const uint64_t&>(obj));
std::cout << "POINTER" << std::endl;
}
};
int main()
{
int x = 0;
Class<int*>::print(&x);
return 0;
}
I made print a template function and it is working as expected.
template <class C>
struct Class<C, typename boost::enable_if_c<boost::is_arithmetic<C>::value || boost::is_pointer<C>::value>::type>
{
template <class T>
static inline typename boost::enable_if_c<boost::is_same<C, T>::value && boost::is_arithmetic<T>::value, void>::type
print(const T& obj)
{
std::cout << "ARITHMETIC TYPE" << std::endl;
}
template <class T>
static inline typename boost::enable_if_c<boost::is_same<C, T>::value && boost::is_pointer<T>::value, void>::type
print(const T& obj)
{
Class<uint64_t>::print(reinterpret_cast<const uint64_t&>(obj));
std::cout << "POINTER" << std::endl;
}
};

the most clean way to static_assert 3 or more items in oneline

How to static_assert 3 items to be same at compile time like this.
union
{
std::uint32_t multibyte;
std::uint8_t bytes[4];
} test;
static_assert(sizeof(test) == sizeof(test.multibyte) == sizeof(test.bytes), "Union size mismatch.");
So of course the static_assert here fails because the last check will be 1 == 4. Is there more clean way besides
static_assert(sizeof(test.bytes) == sizeof(test.multibyte) && sizeof(test) == sizeof(test.bytes), "Union size mismatch.");
You could write a struct for that:
template<typename...> struct AllSame;
template<typename Arg1, typename Arg2, typename... Args>
struct AllSame<Arg1, Arg2, Args...>
{
static constexpr bool value = sizeof(Arg1) == sizeof(Arg2) && AllSame<Arg1, Args...>::value;
};
template<typename Arg>
struct AllSame<Arg>
{
static constexpr bool value = true;
};
Not tested, might contain errors.
If you are able to use c++14 then by the following example you can static_assert instances too, if they can be used in a constant expression:
template<typename T, typename... Ts>
constexpr bool compare_sizes(T&&, Ts&&...) noexcept {
using expand = int[];
bool result = sizeof...(Ts) > 0;
static_cast<void>(expand {
0, (static_cast<void>(result &= (sizeof(Ts) == sizeof(T))), 0)...
});
return result;
}
Example of use with your union { /* ... */ } test:
static_assert(compare_sizes(test, test.multibyte, test.bytes), "Size mismatch.");
Note that variable declaration and use of static_cast statement are prohibited in a constexpr function body until c++14.

SFINAE did not compile [duplicate]

This question already has answers here:
SFINAE working in return type but not as template parameter
(3 answers)
Closed 7 years ago.
Very often I used SFINAE before but I have a very very simple example I can't get to run today.
class X
{
public:
template <typename CHECK, typename = typename std::enable_if< std::is_floating_point<CHECK>::value, void>::type >
void Do()
{
std::cout << "yes" << std::endl;
}
template <typename CHECK, typename = typename std::enable_if< !std::is_floating_point<CHECK>::value, void>::type>
void Do()
{
std::cout<< "no" << std::endl;
}
};
int main()
{
X x;
x.Do<float>();
}
Error:
src/main.cpp:20:18: error: 'template void X::Do()' cannot be overloaded
src/main.cpp:14:18: error: with 'template void X::Do()'
void Do()
I want to disable any overload with enable_if but it doesn't work...
Any idea what today I did wrong?
The two functions have the same sigature, so you get a redefinition error. Try it with the following instead, which uses default arguments:
#include <type_traits>
#include <iostream>
class X
{
public:
template <typename CHECK, std::enable_if_t< std::is_floating_point<CHECK>::value>* =nullptr >
void Do()
{
std::cout << "yes" << std::endl;
}
template <typename CHECK, std::enable_if_t< !std::is_floating_point<CHECK>::value>* =nullptr>
void Do()
{
std::cout<< "no" << std::endl;
}
};
int main()
{
X x;
x.Do<float>();
}
DEMO
See also the answers here and here.
Another syntax which compiles and works is to move the enable_is as the return type:
class X
{
public:
template <typename CHECK >
typename std::enable_if< std::is_floating_point<CHECK>::value, void>::type Do()
{
std::cout << "yes" << std::endl;
}
template <typename CHECK>
typename std::enable_if< !std::is_floating_point<CHECK>::value, void>::type Do()
{
std::cout << "no" << std::endl;
}
};
int main()
{
X x;
x.Do<float>();
getchar();
}

different behaviour for enums and all other types

Using gcc-4.8 with -std=c++11 I want to create a template function with one behaviour for enums and other behaviour for all other types. I try this
#include <type_traits>
#include <iostream>
template<class T, class = typename std::enable_if<std::is_enum<T>::value>::type>
void f(T& /*t*/)
{
std::cout << "enum" << std::endl;
}
template<class T, class = typename std::enable_if<!std::is_enum<T>::value>::type>
void f(T& /*t*/) {
std::cout << "not enum" << std::endl;
}
enum class E
{
A,
B
};
int main()
{
E e;
f(e);
return 0;
}
but compiler returns
1.cpp:11:6: error: redefinition of ‘template<class T, class> void f(T&)’
void f(T& /*t*/) {
^
1.cpp:5:6: error: ‘template<class T, class> void f(T&)’ previously declared here
void f(T& /*t*/)
^
I can comment out first template, it leads to compile error, and it's expectable.
And I also can comment out second template, in this case code code can be compiled.
What do I do wrong?
Because compiler sees them as the same function template, instead, you should do this:
#include <type_traits>
#include <iostream>
template<class T, typename std::enable_if<std::is_enum<T>::value, bool>::type = true>
void f(T& /*t*/)
{
std::cout << "enum" << std::endl;
}
template<class T, typename std::enable_if<!std::is_enum<T>::value, bool>::type = true>
void f(T& /*t*/) {
std::cout << "not enum" << std::endl;
}
enum class E
{
A,
B
};
int main()
{
E e;
f(e);
return 0;
}

Resources