2

나는 std::cout << x을 쓰려고 할 때 발생하는 컴파일 오류에 다소 짜증이 나서 왼쪽 시프트 연산자는 x에 대해 정의되지 않았습니다. x를 (으)로 변환 할 수 없으며 x를 그 것으로 변환 할 수 없습니다 ... 쓸모없는 오류 메시지의 여러 화면.연산자 대체 << 모든 유형의 경우

해당 연산자가 아직 정의되지 않은 모든 유형에 대해 operator<<(std::ostream&, const T&)을 전문화하고 싶습니다. 내부에 정적 어설 션을 넣고 컴파일 오류 메시지를 훨씬 더 선명하게 만들 수 있습니다.

첫 번째 시도는 다음과 같습니다.

template<typename T, typename = void> 
struct Has : public std::false_type {}; 

template<typename T> 
struct Has<T, decltype(void(
      std::declval<std::ostream&>() << std::declval<T>() 
))> : public std::true_type {}; 

template<typename T> 
auto operator<<(std::ostream& out, const T&) 
    -> typename std::enable_if< 
     !Has<T>::value, 
     std::ostream&>::type 
{ 
    return out << "my operator"; 
} 

최대 템플릿 깊이를 초과하기 때문에 컴파일에 실패합니다. 실제로 operator<<Has 전문화를 호출하여 operator<<으로 전화를 걸고 여기서 내 과부하가 다시 검사되는 등 계속 반복됩니다.

가장 간단한 버전도 작동하지 않습니다. std::ostream& << const char*에 대한 모호한 오버로드입니다. 예상 했어.

template<typename T> 
std::ostream& operator<<(std::ostream& out, const T&) 
{ 
    return out << "my operator"; 
} 

어떻게해야합니까? 또는 일반적으로 모든 인수 유형에 대해 함수를 정의 할 수 있지만 함수에 이미 전달 될 수있는 인수 유형에 대해서는 어떻게 정의 할 수 있습니까?

+1

[이발사의 역설] (https://en.wikipedia.org/wiki/Barber_paradox) –

+0

@IgorTandetnik 정확하지는 않습니다. 이발사의 관점에서 볼 때 '이브닝 이발사는 아직 면도하지 않은 모든 사람들을 면도 할 것'이라고 생각합니다. _elsewhere_ 함수에서 허용하지 않는 모든 유형에 대해 함수를 정의하려고합니다. 나는 여기서 역설을 발견하지 못한다. –

+0

잘 구성된 프로그램에서 그러한 연산자를 사용하는 것은 아마도 불가능할 것입니다. 연산자의 정의는 인스턴스화 시점에서 어떤 특수화가 표시되는지에 따라 달라집니다. 번역 단위가 다르거 나 번역 단위가 다를 수도 있습니다 (** ** [포인트]/8 **), 이는 ODR 위반이 될 수 있으며, 프로그램을 부적절하게 만들지 만 진단은 필요하지 않습니다. –

답변

1

일반적으로 할 수 없습니다. 그러나 이것은 이므로 악의가있는 경우 기꺼이 할 수 있습니다.

namespace named_operator { 
    template<class D>struct make_operator{constexpr make_operator(){}}; 

    template<class T, char, class O> struct half_apply { T&& lhs; O const& o; }; 

    template<class Lhs, class Op> 
    half_apply<Lhs, '<', Op> operator<(Lhs&& lhs, make_operator<Op>const & o) { 
    return {std::forward<Lhs>(lhs), o}; 
    } 

    template<class Lhs, class Op, class Rhs> 
    auto operator<(half_apply<Lhs, '<', Op>&& lhs, Rhs&& rhs) 
    -> decltype(named_invoke(std::forward<Lhs>(lhs.lhs), lhs.o, std::forward<Rhs>(rhs))) 
    { 
    return named_invoke(std::forward<Lhs>(lhs.lhs), lhs.o, std::forward<Rhs>(rhs)); 
    } 
} 
namespace utility { 
    namespace details { 
    template<class...>struct voider{using type=void;}; 
    } 
    template<class...Ts>using void_t=typename details::voider<Ts...>::type; 
    namespace details { 

    template<template<class...>class, class, class...> 
    struct can_apply:std::false_type{}; 
    template<template<class...>class Z, class...Ts> 
    struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{}; 
    } 
    template<template<class...>class Z, class...Ts> 
    using can_apply = details::can_apply<Z,void,Ts...>; 
} 
namespace streaming { 
    namespace details { 
    template<class T> 
    using ostream_r = decltype(std::cout << std::declval<T&&>()); 
    } 
    template<class T> 
    using can_ostream = utility::can_apply<details::ostream_r, T>; 

    struct out_tag: named_operator::make_operator<out_tag> {}; 
    static const out_tag out; 
    template<class T> 
    std::ostream& named_invoke(std::ostream& os, out_tag, T const& t) { 
    static_assert(can_ostream<T const&>{}, "This type cannot be streamed"); 
    return os<<t; 
    } 
    template<class T, 
    std::enable_if_t< can_ostream<T const&>{}, int> =0 // breaks MSVC 
    > 
    std::ostream& named_invoke(std::ostream& os, out_tag, T const& t) { 
    return os<<t; 
    } 
} 

와 내가 쓴 경우 그 권리,

struct no_worky {}; 
no_worky bob; 
using streaming::out; 
std::cout <out< bob; 

는 한편

std::cout <out< 7; 

전화 std::cout << 7, 컴파일에 실패하고 친화적 인 메시지를 생성합니다.

나는 이것이 그만한 가치가 있다고 생각하지 않습니다.

+0

방금 ​​프로필의 링크를 따라 명명 된 연산자를 살펴 보았습니다. C++의 어두운면에서 정말 멋진 부분입니다. 그것은 내 문제를 바로 해결하지 못하더라도. 사실, 여기에 이론적 근거가 있습니다. 저는 경쟁 프로그래밍을하고 있습니다. 코드가 컴파일되지 않는 이유를 찾는 데 몇 초가 걸립니다. 따라서 프로덕션 수준의 솔루션을 필요로하지는 않지만 표준 오류에 대해 경고 해주는 특수 해킹이 있습니다. 벡터 및 ODR, 여러 번역 단위 등을 고려할 필요가 없습니다. 당신이 링크 한 질문이 도움이 될지 모르지만, 나는 그것을 지금 검토 중입니다. –

+0

@IvanSmirnov 그런 다음'<< "를 사용하는 대신'print (ostream &, Args const & ...)'를 쓰십시오? – Yakk

0

하나의 대답은 파이프 작동을 위해 얇은 래퍼로 ostream을 래핑하는 것일 수 있습니까?

이 씬 래퍼는 유니버설 템플릿 연산자 < < 멤버 인 을 가질 수 있습니다.이 템플릿은 래핑 된 std :: ostream의 실제 연산자를 차례로 검사합니다. 오류 메시지가 훨씬 덜 우울 할 수도 있습니다!

std :: endl 및 해당 친족에 대한 특정 처리기를 추가해야합니다. 사용자가 혼란스러워하는 스트림 너비를 전문으로하기를 기대합니다.