2012-10-22 4 views
8

C++ 11 기능을 사용하여 사용자 지정 스트림 조작기를보다 쉽게 ​​만들려고합니다. 람다 함수를 조작자로 사용할 수는 있지만 std::function<ostream&(ostream&)>은 사용할 수 없습니다.std :: function을 사용자 지정 스트림 조작자로 사용

여기에 코드를 내려 삶은 것 :

g++-4 src/Solve.cpp -c -g -std=c++0x -o src/Solve.o -I/home/ekrohne/minisat 
src/Solve.cpp: In function 'int main(int, char**)': 
src/Solve.cpp:24:11: error: cannot bind 'std::ostream' lvalue to 'std::basic_ostream<char>&&' 
/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++/ostream:579:5: error: initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = std::function<std::basic_ostream<char>&(std::basic_ostream<char>&)>]' 

왜 실패 않습니다

#include <iostream> 
#include <functional> 
using namespace std; 

auto lambdaManip = [] (ostream& stream) -> ostream& { 
    stream << "Hello world" << endl; 
}; 
function<ostream& (ostream&)> functionManip = [] (ostream& stream) -> ostream& { 
    stream << "Hello world" << endl; 
}; 

int main (int argc, char** argv) { 
    cout << lambdaManip; // OK 
    cout << functionManip; // Compiler error 
} 

두 번째 cout 문은 다음과 실패? Cygwin gcc 4.5.3을 사용하고 있습니다.

효율성 문제로 인해 어디서나 std::function을 사용하는 것에 대해 열망하지는 않습니다. 하지만 λ 함수를 반환하는 함수를 작성하고 싶습니다. std::function 없이는 어떻게해야하는지 잘 모릅니다. 예를 들어, 다음과 같은 것은 훌륭 할 것입니다.

auto getAdditionFunctor(); 

auto getAdditionFunctor() { 
    return [] (int x, int y) { return x + y }; 
}; 

...하지만 분명히 작동하지 않습니다. 작동하는 대체 구문이 있습니까? 나는 그것이 무엇이 될 수 있는지 상상할 수 없다. 그래서 나는 std::function과 붙어 있을지 모른다.

두 번째 질문에 대한 해결책이 있다면 첫 번째 질문은 논박의 여지가 있습니다.


감사합니다.

정의 operator<<(ostream&, std::function<ostream&(ostream&)> 도움. 나는 웹 페이지를 잘못 읽고, ostreamoperator()을 조작자로 가지는 임의의 객체를 처리하기에 충분히 똑똑하다는 인상 아래에있었습니다. 나는 이것에 대해 틀 렸습니다. 게다가, 내가 만든 단순한 lambda은 아마도 내가 말했던 것처럼 평범한 오래된 함수로 컴파일되었을 것입니다. 실제로 변수 캡처를 사용하여 lambda이 단순 함수가 아닌지 확인하면 컴파일러가 실패합니다. 또한, 정의 operator()와 객체는하지 (기본적으로) 매니퓰레이터로 취급됩니다

class Manipulator { 
    ostream& operator()(ostream& stream) const { 
     return stream << "Hello world" << endl; 
    }; 
} classManip; 

function<ostream& (ostream&)> functionManip = [] (ostream& stream) -> ostream& { 
    return stream << "Hello world" << endl; 
}; 

int main (int argc, char** argv) { 
    const string str = "Hello world"; 
    auto lambdaManip = [&] (ostream& stream) -> ostream& { 
     return stream << str << endl;  
    }; 

    cout << classManip;  // Compiler error 
    cout << lambdaManip; // Compiler error 
    cout << functionManip; // Compiler error 
} 

또한 갱신 :

// Tell ostreams to interpret std::function as a 
// manipulator, wherever it sees one. 
inline ostream& operator<<(
     ostream& stream, 
     const function<ostream& (ostream&)>& manipulator) { 
    return manipulator(stream); 
} 
: 그것은으로 수행 할 수 있습니다 아래의 것보다 약간 더 강력한 솔루션을 밝혀

이 코드에는 const이 여분으로 있습니다. 내 프로젝트에서 실제로이 솔루션을 구현하려고 시도하는 것을 발견했습니다. 당신이 ostream에 대한 operator<< 보면

+4

해당 조작자에 '반품'을 남기고 싶습니까? –

+0

나는 그것들이 함축되어 있다는 것을 읽었으며 그것들을 추가하는 것이 도움이되지 않는다. 이 경우 나는 그들이 반환과 함께 더 읽기 쉽다고 생각하지만, 그들은 합법적이지 않다. 결국,'cout << functionManip; '을 주석 처리하면 모든 것이 제대로 작동합니다. –

+3

@EdKrohne :'return'은 ** 여기서는 선택 사항이 아닙니다 ** - 람다는 다른 함수와 마찬가지로 값을 반환하지 않고 'void'가 아닌 반환 유형을 사용하여 UB를 호출합니다. §6.6.3/2 : "* 함수의 끝에서 벗어나는 것은 값이없는'return'과 같습니다 : 이것은 값을 반환하는 함수에서 정의되지 않은 행동을합니다. *"잘 작동하는 것처럼 보이는 것은 단지 하나의 가능한 표현입니다 UB의 – ildjarn

답변

7

std::function을 복용에 대한 과부하가 없습니다 - 그것은 당신이 cout << functionManip 여기 뭘 하려는지 기본적입니다.이 문제를 해결, 중 과부하 자신을 정의하려면

ostream& operator<<(ostream& os, std::function<ostream& (ostream&)>& s) 
{ 
    return s(os); 
} 

을 또는 함수의 인수로 stream을 통과 : 주어진 lambda가 작동하는 이유에 대해서는

functionManip(std::cout); 

하는의 반환 형식 lambda은 정의하며, 과부하는 함수 포인터 이용해있다 :

ostream& operator<< (ostream& (*pf)(ostream&)); 

람다 아마도 모두의 utili 배치된다 구조체를 zing하고 operator()을 정의하면이 경우 함수 포인터와 똑같이 작동합니다. 이것은 나에게 가장 유력한 설명이며, 만약 내가 틀렸다면 누군가가 나를 교정 할 수 있기를 바랍니다.

+1

행동은 우발적 인 것이 아니라 표준에 의해 위임되었습니다. 아무 것도 포착하지 않는 람다는 평범한 구 함수 포인터로 항상 암시 적으로 변환 가능합니다. (그리고 반대로 : * 아무것도 잡아 내지 않는 람다는 암묵적으로 함수 포인터로 변환 될 수 없다.) – Quuxplusone

5

이 오류의 원인이되지 않습니다,하지만 당신은 반환 형식 ostream&있는 것으로 lambdaManipfunctionManip 모두를 정의한 이후는 모두 return stream;을 추가 잊어 버린 생각합니다. 정의 된 operator<<(ostream&, std::function<ostream&(ostream&)>가 없기 때문에


호출 cout << functionManip이 실패합니다. 하나를 추가하면 호출이 성공합니다.

ostream& operator<<(ostream& stream, function<ostream& (ostream&)>& func) { 
    return func(stream); 
} 

또는, 이것은 operator<< 정의를 추가하지 않고 작동합니다 functionManip

functionManip(cout); 

로 호출 할 수 있습니다. getAdditionFunctor에 의해 반환되는 람다 캡처없는 람다 때문에, 람다 반환에 대한 질문에 대해서는

, 그것은 암시 적으로 함수 포인터로 변환 할 수 있습니다.

typedef int(*addition_ptr)(int,int); 
addition_ptr getAdditionFunctor() 
{ 
    return [] (int x, int y) -> int { return x + y; }; 
} 

auto adder = getAdditionFunctor(); 
adder(10,20); // Outputs 30 
+0

좋은 점은, 나는 그 장난감 예제에 대해 생각해 봤어야했다. 하지만 내가 붙잡는다면? 변수 캡처는 함수에서'lambda '를 반환하는 지점입니다. 가변 캡쳐를하고 싶지 않다면, 처음에는 일반 함수를 사용하고 싶습니다. –

+0

+1. 캡쳐리스 람다가 암시 적으로 함수 포인터로 변환 될 수 있다는 것을 알지 못했지만 완벽하게 이해할 수 있습니다. – Yuushi

3

이전 답변은 예술 오른쪽의 상태를 가지고,하지만 당신은 std::function의 및/또는 코드에서 스트림 조종 등의 람다를 사용하여 크게 될 위하여려고하는 경우에, 당신은 단지를 추가 할 수 있습니다 전역 범위에서, 당신의 코드베이스에 함수 템플릿 정의를 다음

template<class M> 
auto operator<< (std::ostream& os, const M& m) -> decltype(m(os)) 
{ 
    return m(os); 
} 

expression SFINAE을 사용하기 때문에 m(os)가 잘 형성 표현하지 않는 한이 특정 operator<< 과부하도 오버로드 확인에 참여하지 것이다 뒤에 반환 형식 . (이것은 당신이 원하는 것입니다.)

그런 다음 당신도 (

template<class T> 
auto commaize(const T& t) 
{ 
    return [&t](std::ostream& os) -> std::ostream& { 
     return os << t << ", "; 
    }; 
} 

int main() 
{ 
    std::cout << commaize("hello") << commaize("darling") << std::endl; 
} 

같은 일을 위의 코드에있는 std::function 개체의 총 부족을 알 수 있습니다! 당신이하지 않는 std::function 객체로 스터핑 람다를 피하십시오 왜냐하면 std::function 객체를 생성하는 것이 값 비싸기 때문에 메모리 할당과 관련 될 수 있기 때문입니다.)

C++ 17에서 표준 라이브러리에 operator<< 오버로드를 추가하는 것이 합리적입니다. 그 지역에있는 어떤 구체적인 제안서의 nt.

관련 문제